Repository: pydicom/pylibjpeg-openjpeg (language: python)

Issue:
Add encoding for JPEG 2000 Lossless transfer syntax



Discussion / hints:
Hi @scaramallion, we would be very interested in having this feature available for some applications in [highdicom](https://github.com/ImagingDataCommons/highdicom). What would it take to enable it?

If feasible, we may be able to help to some extent.

cc @dclunie @fedorov

(edit: i should clarify that we are particularly interested in single bit JPEG2000 compression for use with DICOM segmentation files, which cannot currently be done with Pillow)
You'd need to write the encoder and Python interface and supporting tests. Optionally you could add an encoder plugin to pydicom but strictly speaking that's not necessary.

If it were me, I'd start by looking at the DICOM Standard, and what openjpeg can do (I don't think it can handle more than 31-bit data, for example), and decide which subset of functionality you immediately want to support.

Ideally 8 and 16-bit signed/unsigned monochrome and three channel images to start with. You'd obviously want to add support for 1-bit segs as well.
For reference, [here is the table of pixel data attributes](https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_8.2.4.html) that can be used with J2K in DICOM
FYI, that table is not quite correct, since I inadvertently specified some restrictions when I did CP 1653 that added this summary table, which are not actually present. See CP [2301](https://dicom.nema.org/medical/dicom/CP/cp2301_vp_singleBitRLEandJ2KforSEG.pdf).

Fix patch:
diff --git a/README.md b/README.md
index 3f09b55..5680ba0 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,13 @@ python -m pip install pylibjpeg-openjpeg
 | [15444-1](https://www.iso.org/standard/78321.html) | [T.800](https://www.itu.int/rec/T-REC-T.800/en) | [JPEG 2000](https://jpeg.org/jpeg2000/) |
 
 #### Encoding
-Encoding of JPEG 2000 images is not currently supported
+
+Encoding of NumPy ndarrays is supported for the following:
+
+* Array dtype: bool, uint8, int8, uint16, int16, uint32 and int32 (1-24 bit-depth only)
+* Array shape: (rows, columns) and (rows, columns, planes)
+* Number of rows/columns: up to 65535
+* Number of planes: 1, 3 or 4
 
 
 ### Transfer Syntaxes
@@ -81,3 +87,46 @@ with open('filename.j2k', 'rb') as f:
 # Or simply...
 arr = decode('filename.j2k')
 ```
+
+#### Standalone JPEG encoding
+
+Lossless encoding of RGB with multiple-component transformation:
+
+```python
+
+import numpy as np
+from openjpeg import encode
+
+arr = np.random.randint(low=0, high=65535, size=(100, 100, 3), dtype="uint8")
+encode(arr, photometric_interpretation=1)  # 1: sRGB
+```
+
+Lossy encoding of a monochrome image using compression ratios:
+
+```python
+
+import numpy as np
+from openjpeg import encode
+
+arr = np.random.randint(low=-2**15, high=2**15 - 1, size=(100, 100), dtype="int8")
+# You must determine your own values for `compression_ratios`
+#   as these are for illustration purposes only
+encode(arr, compression_ratios=[2, 4, 6])
+```
+
+Lossy encoding of a monochrome image using peak signal-to-noise ratios:
+
+```python
+
+import numpy as np
+from openjpeg import encode
+
+arr = np.random.randint(low=-2**15, high=2**15 - 1, size=(100, 100), dtype="int8")
+# You must determine your own values for `signal_noise_ratios`
+#   as these are for illustration purposes only
+encode(arr, signal_noise_ratios=[50, 80, 100])
+```
+
+See the docstring for the [encode() function][2] for full details.
+
+[2]: https://github.com/pydicom/pylibjpeg-openjpeg/blob/main/openjpeg/utils.py#L428
diff --git a/build.py b/build.py
index 6e6e9f7..7832954 100644
--- a/build.py
+++ b/build.py
@@ -63,7 +63,9 @@ def get_source_files() -> List[Path]:
     """Return a list of paths to the source files to be compiled."""
     source_files = [
         INTERFACE_SRC / "decode.c",
+        INTERFACE_SRC / "encode.c",
         INTERFACE_SRC / "color.c",
+        INTERFACE_SRC / "utils.c",
     ]
     for fname in OPENJPEG_SRC.glob("*"):
         if fname.parts[-1].startswith("test"):
diff --git a/docs/changes/v2.1.0.rst b/docs/changes/v2.1.0.rst
new file mode 100644
index 0000000..db48c34
--- /dev/null
+++ b/docs/changes/v2.1.0.rst
@@ -0,0 +1,14 @@
+.. _v2.1.0:
+
+2.1.0
+=====
+
+Changes
+.......
+
+* Added support for encoding a numpy ndarray using JPEG2000 lossless and lossy
+  * Supported array shapes are (rows, columns) and (rows, columns, planes)
+  * Supported number of planes is 1, 3 and 4
+  * Supported dtypes are bool, u1, i1, u2, i2 for bit-depths 1-16
+  * Also supported are u4 and i4 for bit-depths 1-24
+* Added support for decoding JPEG2000 data with precision up to 24-bits
diff --git a/lib/interface/decode.c b/lib/interface/decode.c
index 9bb6ceb..47cf301 100644
--- a/lib/interface/decode.c
+++ b/lib/interface/decode.c
@@ -47,10 +47,24 @@ BSD license (see the main LICENSE file).
 #include <stdio.h>
 #include <../openjpeg/src/lib/openjp2/openjpeg.h>
 #include "color.h"
+#include "utils.h"
 
 
-// Size of the buffer for the input stream
-#define BUFFER_SIZE OPJ_J2K_STREAM_CHUNK_SIZE
+static void py_error(const char *msg) {
+    py_log("openjpeg.decode", "ERROR", msg);
+}
+
+static void info_callback(const char *msg, void *callback) {
+    py_log("openjpeg.decode", "INFO", msg);
+}
+
+static void warning_callback(const char *msg, void *callback) {
+    py_log("openjpeg.decode", "WARNING", msg);
+}
+
+static void error_callback(const char *msg, void *callback) {
+    py_error(msg);
+}
 
 
 const char * OpenJpegVersion(void)
@@ -103,196 +117,6 @@ typedef struct opj_decompress_params {
 } opj_decompress_parameters;
 
 
-// Python stream methods
-static Py_ssize_t py_tell(PyObject *stream)
-{
-    /* Return the current position of the `stream`.
-
-    Parameters
-    ----------
-    stream : PyObject *
-        The Python stream object to use (must have a ``tell()`` method).
-
-    Returns
-    -------
-    Py_ssize_t
-        The new position in the `stream`.
-    */
-    PyObject *result;
-    Py_ssize_t location;
-
-    result = PyObject_CallMethod(stream, "tell", NULL);
-    location = PyLong_AsSsize_t(result);
-
-    Py_DECREF(result);
-
-    //printf("py_tell(): %u\n", location);
-    return location;
-}
-
-
-static OPJ_SIZE_T py_read(void *destination, OPJ_SIZE_T nr_bytes, void *fd)
-{
-    /* Read `nr_bytes` from Python object `fd` and copy it to `destination`.
-
-    Parameters
-    ----------
-    destination : void *
-        The object where the read data will be copied.
-    nr_bytes : OPJ_SIZE_T
-        The number of bytes to be read.
-    fd : PyObject *
-        The Python file-like to read the data from (must have a ``read()``
-        method).
-
-    Returns
-    -------
-    OPJ_SIZE_T
-        The number of bytes read or -1 if reading failed or if trying to read
-        while at the end of the data.
-    */
-    PyObject* result;
-    char* buffer;
-    Py_ssize_t length;
-    int bytes_result;
-
-    // Py_ssize_t: signed int samed size as size_t
-    // fd.read(nr_bytes), "k" => C unsigned long int to Python int
-    result = PyObject_CallMethod(fd, "read", "n", nr_bytes);
-    // Returns the null-terminated contents of `result`
-    // `length` is Py_ssize_t *
-    // `buffer` is char **
-    // If `length` is NULL, returns -1
-    bytes_result = PyBytes_AsStringAndSize(result, &buffer, &length);
-
-    // `length` is NULL
-    if (bytes_result == -1)
-        goto error;
-
-    // More bytes read then asked for
-    if (length > (long)(nr_bytes))
-        goto error;
-
-    // Convert `length` to OPJ_SIZE_T - shouldn't have negative lengths
-    if (length < 0)
-        goto error;
-
-    OPJ_SIZE_T len_size_t = (OPJ_SIZE_T)(length);
-
-    // memcpy(void *dest, const void *src, size_t n)
-    memcpy(destination, buffer, len_size_t);
-
-    //printf("py_read(): %u bytes asked, %u bytes read\n", nr_bytes, len_size_t);
-
-    Py_DECREF(result);
-    return len_size_t ? len_size_t: (OPJ_SIZE_T)-1;
-
-error:
-    Py_DECREF(result);
-    return -1;
-}
-
-
-static OPJ_BOOL py_seek(Py_ssize_t offset, void *stream, int whence)
-{
-    /* Change the `stream` position to the given `offset` from `whence`.
-
-    Parameters
-    ----------
-    offset : OPJ_OFF_T
-        The offset relative to `whence`.
-    stream : PyObject *
-        The Python stream object to seek (must have a ``seek()`` method).
-    whence : int
-        0 for SEEK_SET, 1 for SEEK_CUR, 2 for SEEK_END
-
-    Returns
-    -------
-    OPJ_TRUE : OBJ_BOOL
-    */
-    // Python and C; SEEK_SET is 0, SEEK_CUR is 1 and SEEK_END is 2
-    // fd.seek(nr_bytes),
-    // k: convert C unsigned long int to Python int
-    // i: convert C int to a Python integer
-    PyObject *result;
-    result = PyObject_CallMethod(stream, "seek", "ni", offset, whence);
-    Py_DECREF(result);
-
-    //printf("py_seek(): offset %u bytes from %u\n", offset, whence);
-
-    return OPJ_TRUE;
-}
-
-
-static OPJ_BOOL py_seek_set(OPJ_OFF_T offset, void *stream)
-{
-    /* Change the `stream` position to the given `offset` from SEEK_SET.
-
-    Parameters
-    ----------
-    offset : OPJ_OFF_T
-        The offset relative to SEEK_SET.
-    stream : PyObject *
-        The Python stream object to seek (must have a ``seek()`` method).
-
-    Returns
-    -------
-    OPJ_TRUE : OBJ_BOOL
-    */
-    return py_seek(offset, stream, SEEK_SET);
-}
-
-
-static OPJ_OFF_T py_skip(OPJ_OFF_T offset, void *stream)
-{
-    /* Change the `stream` position by `offset` from SEEK_CUR and return the
-    new position.
-
-    Parameters
-    ----------
-    offset : OPJ_OFF_T
-        The offset relative to SEEK_CUR.
-    stream : PyObject *
-        The Python stream object to seek (must have a ``seek()`` method).
-
-    Returns
-    -------
-    off_t
-        The new position in the `stream`.
-    */
-    py_seek(offset, stream, SEEK_CUR);
-
-    off_t pos;
-    pos = py_tell(stream);
-
-    return pos ? pos : (OPJ_OFF_T) -1;
-}
-
-
-static OPJ_UINT64 py_length(PyObject * stream)
-{
-    /* Return the total length of the `stream`.
-
-    Parameters
-    ----------
-    stream : PyObject *
-        The Python stream object (must have ``seek()`` and ``tell()`` methods).
-
-    Returns
-    -------
-    OPJ_UINT64
-        The total length of the `stream`.
-    */
-    OPJ_OFF_T input_length = 0;
-
-    py_seek(0, stream, SEEK_END);
-    input_length = (OPJ_OFF_T)py_tell(stream);
-    py_seek(0, stream, SEEK_SET);
-
-    return (OPJ_UINT64)input_length;
-}
-
-
 // Decoding stuff
 static void set_default_parameters(opj_decompress_parameters* parameters)
 {
@@ -360,7 +184,7 @@ extern int GetParameters(PyObject* fd, int codec_format, j2k_parameters_t *outpu
     opj_decompress_parameters parameters;
     set_default_parameters(&parameters);
 
-    int error_code = EXIT_FAILURE;
+    int return_code = EXIT_FAILURE;
 
     // Creates an abstract input stream; allocates memory
     stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
@@ -368,7 +192,7 @@ extern int GetParameters(PyObject* fd, int codec_format, j2k_parameters_t *outpu
     if (!stream)
     {
         // Failed to create the input stream
-        error_code = 1;
+        return_code = 1;
         goto failure;
     }
 
@@ -385,7 +209,7 @@ extern int GetParameters(PyObject* fd, int codec_format, j2k_parameters_t *outpu
     if (!opj_setup_decoder(codec, &(parameters.core)))
     {
         // failed to setup the decoder
-        error_code = 2;
+        return_code = 2;
         goto failure;
     }
 
@@ -393,7 +217,7 @@ extern int GetParameters(PyObject* fd, int codec_format, j2k_parameters_t *outpu
     if (!opj_read_header(stream, codec, &image))
     {
         // failed to read the header
-        error_code = 3;
+        return_code = 3;
         goto failure;
     }
 
@@ -421,7 +245,7 @@ extern int GetParameters(PyObject* fd, int codec_format, j2k_parameters_t *outpu
         if (stream)
             opj_stream_destroy(stream);
 
-        return error_code;
+        return return_code;
 }
 
 
@@ -462,7 +286,6 @@ static opj_image_t* upsample_image_components(opj_image_t* original)
         opj_image_cmptparm_t* l_new_cmp = &(l_new_components[ii]);
         opj_image_comp_t* l_org_cmp = &(original->comps[ii]);
 
-        l_new_cmp->bpp = l_org_cmp->bpp;
         l_new_cmp->prec = l_org_cmp->prec;
         l_new_cmp->sgnd = l_org_cmp->sgnd;
         l_new_cmp->x0 = original->x0;
@@ -659,15 +482,19 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     // Array of pointers to the first element of each component
     int **p_component = NULL;
 
-    int error_code = EXIT_FAILURE;
+    int return_code = EXIT_FAILURE;
+
+    /* Send info, warning, error message to Python logging */
+    opj_set_info_handler(codec, info_callback, NULL);
+    opj_set_warning_handler(codec, warning_callback, NULL);
+    opj_set_error_handler(codec, error_callback, NULL);
 
     // Creates an abstract input stream; allocates memory
     stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
-
     if (!stream)
     {
         // Failed to create the input stream
-        error_code = 1;
+        return_code = 1;
         goto failure;
     }
 
@@ -686,7 +513,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     if (!opj_setup_decoder(codec, &(parameters.core)))
     {
         // failed to setup the decoder
-        error_code = 2;
+        return_code = 2;
         goto failure;
     }
 
@@ -694,7 +521,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     if (! opj_read_header(stream, codec, &image))
     {
         // failed to read the header
-        error_code = 3;
+        return_code = 3;
         goto failure;
     }
 
@@ -708,7 +535,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
             )
         {
             // failed to set the component indices
-            error_code = 4;
+            return_code = 4;
             goto failure;
         }
     }
@@ -722,7 +549,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
         )
     {
         // failed to set the decoded area
-        error_code = 5;
+        return_code = 5;
         goto failure;
     }
 
@@ -730,7 +557,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     if (!(opj_decode(codec, stream, image) && opj_end_decompress(codec, stream)))
     {
         // failed to decode image
-        error_code = 6;
+        return_code = 6;
         goto failure;
     }
 
@@ -753,7 +580,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     image = upsample_image_components(image);
     if (image == NULL) {
         // failed to upsample image
-        error_code = 8;
+        return_code = 8;
         goto failure;
     }
 
@@ -774,9 +601,9 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
     //  we have R1, B1, G1 | R2, G2, B2 | ..., where 1 is the first pixel,
     //  2 the second, etc
     // See DICOM Standard, Part 3, Annex C.7.6.3.1.3
-    unsigned int row, col, ii;
-    if (precision <= 8)
-    {
+    int row, col;
+    unsigned int ii;
+    if (precision <= 8) {
         // 8-bit signed/unsigned
         for (row = 0; row < height; row++)
         {
@@ -790,9 +617,7 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
                 }
             }
         }
-    }
-    else if (precision <= 16)
-    {
+    } else if (precision <= 16) {
         union {
             unsigned short val;
             unsigned char vals[2];
@@ -815,11 +640,36 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
                 }
             }
         }
-    }
-    else
-    {
+    } else if (precision <= 32) {
+        union {
+            unsigned long val;
+            unsigned char vals[4];
+        } u32;
+
+        // 32-bit signed/unsigned
+        for (row = 0; row < height; row++)
+        {
+            for (col = 0; col < width; col++)
+            {
+                for (ii = 0; ii < NR_COMPONENTS; ii++)
+                {
+                    u32.val = (unsigned long)(*p_component[ii]);
+                    // Little endian output
+                    *out = u32.vals[0];
+                    out++;
+                    *out = u32.vals[1];
+                    out++;
+                    *out = u32.vals[2];
+                    out++;
+                    *out = u32.vals[3];
+                    out++;
+                    p_component[ii]++;
+                }
+            }
+        }
+    } else {
         // Support for more than 16-bits per component is not implemented
-        error_code = 7;
+        return_code = 7;
         goto failure;
     }
 
@@ -849,5 +699,5 @@ extern int Decode(PyObject* fd, unsigned char *out, int codec_format)
         if (stream)
             opj_stream_destroy(stream);
 
-        return error_code;
+        return return_code;
 }
diff --git a/lib/interface/encode.c b/lib/interface/encode.c
new file mode 100644
index 0000000..b611fb2
--- /dev/null
+++ b/lib/interface/encode.c
@@ -0,0 +1,466 @@
+
+
+#include "Python.h"
+#include "numpy/ndarrayobject.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <../openjpeg/src/lib/openjp2/openjpeg.h>
+#include "utils.h"
+
+
+static void py_debug(const char *msg) {
+    py_log("openjpeg.encode", "DEBUG", msg);
+}
+
+static void py_error(const char *msg) {
+    py_log("openjpeg.encode", "ERROR", msg);
+}
+
+static void info_callback(const char *msg, void *callback) {
+    py_log("openjpeg.encode", "INFO", msg);
+}
+
+static void warning_callback(const char *msg, void *callback) {
+    py_log("openjpeg.encode", "WARNING", msg);
+}
+
+static void error_callback(const char *msg, void *callback) {
+    py_error(msg);
+}
+
+
+extern int Encode(
+    PyArrayObject *arr,
+    PyObject *dst,
+    int bits_stored,
+    int photometric_interpretation,
+    int use_mct,
+    PyObject *compression_ratios,
+    PyObject *signal_noise_ratios,
+    int codec_format
+)
+{
+    /* Encode a numpy ndarray using JPEG 2000.
+
+    Parameters
+    ----------
+    arr : PyArrayObject *
+        The numpy ndarray containing the image data to be encoded.
+    dst : PyObject *
+        The destination for the encoded codestream, should be a BinaryIO.
+    bits_stored : int
+        Supported values: 1-16
+    photometric_interpretation : int
+        Supported values: 0-5
+    use_mct : int
+        Supported values 0-1, can't be used with subsampling
+    compression_ratios : list[float]
+        Encode lossy with the specified compression ratio for each layer. The
+        ratio should be decreasing with increasing layer, and may be 1 to signal
+        the use of lossless encoding for that layer.
+    signal_noise_ratios : list[float]
+        Encode lossy with the specified peak signal-to-noise ratio. The ratio
+        should be increasing with increasing layer, but may be 0 in the final
+        layer to signal the use of lossless encoding for that layer.
+    codec_format : int
+        The format of the encoded JPEG 2000 data, one of:
+        * ``0`` - OPJ_CODEC_J2K : JPEG-2000 codestream
+        * ``2`` - OPJ_CODEC_JP2 : JP2 file format
+
+    Returns
+    -------
+    int
+        The exit status, 0 for success, failure otherwise.
+    */
+
+    // Check input
+    // Determine the number of dimensions in the array, should be 2 or 3
+    int nd = PyArray_NDIM(arr);
+
+    // Can return NULL for 0-dimension arrays
+    npy_intp *shape = PyArray_DIMS(arr);
+    OPJ_UINT32 rows = 0;
+    OPJ_UINT32 columns = 0;
+    unsigned int samples_per_pixel = 0;
+    switch (nd) {
+        case 2: {
+            // (rows, columns)
+            samples_per_pixel = 1;
+            rows = (OPJ_UINT32) shape[0];
+            columns = (OPJ_UINT32) shape[1];
+            break;
+        }
+        case 3: {
+            // (rows, columns, planes)
+            // Only allow 3 or 4 samples per pixel
+            if ( shape[2] != 3 && shape[2] != 4 ) {
+                py_error(
+                    "The input array has an unsupported number of samples per pixel"
+                );
+                return 1;
+            }
+            rows = (OPJ_UINT32) shape[0];
+            columns = (OPJ_UINT32) shape[1];
+            samples_per_pixel = (unsigned int) shape[2];
+            break;
+        }
+        default: {
+            py_error("An input array with the given dimensions is not supported");
+            return 2;
+        }
+    }
+
+    // Check number of rows and columns is in (1, 65535)
+    if (rows < 1 || rows > 65535) {
+        py_error("The input array has an unsupported number of rows");
+        return 3;
+    }
+    if (columns < 1 || columns > 65535) {
+        py_error("The input array has an unsupported number of columns");
+        return 4;
+    }
+
+    // Check the dtype is supported
+    PyArray_Descr *dtype = PyArray_DTYPE(arr);
+    int type_enum = dtype->type_num;
+    switch (type_enum) {
+        case NPY_BOOL:  // bool
+        case NPY_INT8:  // i1
+        case NPY_UINT8:  // u1
+        case NPY_INT16:  // i2
+        case NPY_UINT16:  // u2
+        case NPY_INT32:  // i4
+        case NPY_UINT32:  // u4
+            break;
+        default:
+            py_error("The input array has an unsupported dtype");
+            return 5;
+    }
+
+    // Check array is C-style, contiguous and aligned
+    if (PyArray_ISCARRAY_RO(arr) != 1) {
+        py_error("The input array must be C-style, contiguous and aligned");
+        return 7;
+    };
+
+    int bits_allocated;
+    if (type_enum == NPY_BOOL || type_enum == NPY_INT8 || type_enum == NPY_UINT8) {
+        bits_allocated = 8;  // bool, i1, u1
+    } else if (type_enum == NPY_INT16 || type_enum == NPY_UINT16 )  {
+        bits_allocated = 16;  // i2, u2
+    } else {
+        bits_allocated = 32;  // i4, u4
+    }
+
+    // Check `photometric_interpretation` is valid
+    if (
+        samples_per_pixel == 1
+        && (
+            photometric_interpretation != 0  // OPJ_CLRSPC_UNSPECIFIED
+            && photometric_interpretation != 2  // OPJ_CLRSPC_GRAY
+        )
+    ) {
+        py_error(
+            "The value of the 'photometric_interpretation' parameter is not "
+            "valid for the number of samples per pixel"
+        );
+        return 9;
+    }
+
+    if (
+        samples_per_pixel == 3
+        && (
+            photometric_interpretation != 0  // OPJ_CLRSPC_UNSPECIFIED
+            && photometric_interpretation != 1  // OPJ_CLRSPC_SRGB
+            && photometric_interpretation != 3  // OPJ_CLRSPC_SYCC
+            && photometric_interpretation != 4  // OPJ_CLRSPC_EYCC
+        )
+    ) {
+        py_error(
+            "The value of the 'photometric_interpretation' parameter is not "
+            "valid for the number of samples per pixel"
+        );
+        return 9;
+    }
+
+    if (
+        samples_per_pixel == 4
+        && (
+            photometric_interpretation != 0  // OPJ_CLRSPC_UNSPECIFIED
+            && photometric_interpretation != 5  // OPJ_CLRSPC_CMYK
+        )
+    ) {
+        py_error(
+            "The value of the 'photometric_interpretation' parameter is not "
+            "valid for the number of samples per pixel"
+        );
+        return 9;
+    }
+
+    // Disable MCT if the input is not RGB
+    if (samples_per_pixel != 3 || photometric_interpretation != 1) {
+        use_mct = 0;
+    }
+
+    // Check the encoding format
+    if (codec_format != 0 && codec_format != 2) {
+        py_error("The value of the 'codec_format' parameter is invalid");
+        return 10;
+    }
+
+    unsigned int is_signed;
+    if (type_enum == NPY_INT8 || type_enum == NPY_INT16 || type_enum == NPY_INT32) {
+      is_signed = 1;
+    } else {
+      is_signed = 0;
+    }
+
+    // Encoding parameters
+    unsigned int return_code;
+    opj_cparameters_t parameters;
+    opj_stream_t *stream = 00;
+    opj_codec_t *codec = 00;
+    opj_image_t *image = NULL;
+
+    // subsampling_dx 1
+    // subsampling_dy 1
+    // tcp_numlayers = 0
+    // tcp_rates[0] = 0
+    // prog_order = OPJ_LRCP
+    // cblockw_init = 64
+    // cblockh_init = 64
+    // numresolution = 6
+    opj_set_default_encoder_parameters(&parameters);
+
+    // Set MCT and codec
+    parameters.tcp_mct = use_mct;
+    parameters.cod_format = codec_format;
+
+    // Set up for lossy (if applicable)
+    Py_ssize_t nr_cr_layers = PyObject_Length(compression_ratios);
+    Py_ssize_t nr_snr_layers = PyObject_Length(signal_noise_ratios);
+    if (nr_cr_layers > 0 || nr_snr_layers > 0) {
+        // Lossy compression using compression ratios
+        parameters.irreversible = 1;  // use DWT 9-7
+        if (nr_cr_layers > 0) {
+            if (nr_cr_layers > 100) {
+                return_code = 11;
+                goto failure;
+            }
+
+            parameters.cp_disto_alloc = 1;  // Allocation by rate/distortion
+            parameters.tcp_numlayers = nr_cr_layers;
+            for (int idx = 0; idx < nr_cr_layers; idx++) {
+                PyObject *item = PyList_GetItem(compression_ratios, idx);
+                if (item == NULL || !PyFloat_Check(item)) {
+                    return_code = 12;
+                    goto failure;
+                }
+                double value = PyFloat_AsDouble(item);
+                if (value < 1 || value > 1000) {
+                    return_code = 13;
+                    goto failure;
+                }
+                // Maximum 100 rates
+                parameters.tcp_rates[idx] = value;
+            }
+            py_debug("Encoding using lossy compression based on compression ratios");
+
+        } else {
+            // Lossy compression using peak signal-to-noise ratios
+            if (nr_snr_layers > 100) {
+                return_code = 14;
+                goto failure;
+            }
+
+            parameters.cp_fixed_quality = 1;
+            parameters.tcp_numlayers = nr_snr_layers;
+            for (int idx = 0; idx < nr_snr_layers; idx++) {
+                PyObject *item = PyList_GetItem(signal_noise_ratios, idx);
+                if (item == NULL || !PyFloat_Check(item)) {
+                    return_code = 15;
+                    goto failure;
+                }
+                double value = PyFloat_AsDouble(item);
+                if (value < 0 || value > 1000) {
+                    return_code = 16;
+                    goto failure;
+                }
+                // Maximum 100 ratios
+                parameters.tcp_distoratio[idx] = value;
+            }
+            py_debug(
+                "Encoding using lossy compression based on peak signal-to-noise ratios"
+            );
+        }
+    }
+
+    py_debug("Input validation complete, setting up for encoding");
+
+    // Create the input image and configure it
+    // Setup the parameters for each image component
+    opj_image_cmptparm_t *cmptparm;
+    cmptparm = (opj_image_cmptparm_t*) calloc(
+        (OPJ_UINT32) samples_per_pixel,
+        sizeof(opj_image_cmptparm_t)
+    );
+    if (!cmptparm) {
+        py_error("Failed to assign the image component parameters");
+        return_code = 20;
+        goto failure;
+    }
+    unsigned int i;
+    for (i = 0; i < samples_per_pixel; i++) {
+        cmptparm[i].prec = (OPJ_UINT32) bits_stored;
+        cmptparm[i].sgnd = (OPJ_UINT32) is_signed;
+        // Sub-sampling: none
+        cmptparm[i].dx = 1;
+        cmptparm[i].dy = 1;
+        cmptparm[i].w = columns;
+        cmptparm[i].h = rows;
+    }
+
+    // Create the input image object
+    image = opj_image_create(
+        (OPJ_UINT32) samples_per_pixel,
+        &cmptparm[0],
+        photometric_interpretation
+    );
+
+    free(cmptparm);
+    if (!image) {
+        py_error("Failed to create an empty image object");
+        return_code = 21;
+        goto failure;
+    }
+
+    /* set image offset and reference grid */
+    image->x0 = (OPJ_UINT32)parameters.image_offset_x0;
+    image->y0 = (OPJ_UINT32)parameters.image_offset_y0;
+    image->x1 = (OPJ_UINT32)parameters.image_offset_x0 + (OPJ_UINT32)columns;
+    image->y1 = (OPJ_UINT32)parameters.image_offset_y0 + (OPJ_UINT32)rows;
+
+    // Add the image data
+    void *ptr;
+    unsigned int p, r, c;
+    if (bits_allocated == 8) {  // bool, u1, i1
+        for (p = 0; p < samples_per_pixel; p++)
+        {
+            for (r = 0; r < rows; r++)
+            {
+                for (c = 0; c < columns; c++)
+                {
+                    ptr = PyArray_GETPTR3(arr, r, c, p);
+                    image->comps[p].data[c + columns * r] = is_signed ? *(npy_int8 *) ptr : *(npy_uint8 *) ptr;
+                }
+            }
+        }
+    } else if (bits_allocated == 16) {  // u2, i2
+        for (p = 0; p < samples_per_pixel; p++)
+        {
+            for (r = 0; r < rows; r++)
+            {
+                for (c = 0; c < columns; c++)
+                {
+                    ptr = PyArray_GETPTR3(arr, r, c, p);
+                    image->comps[p].data[c + columns * r] = is_signed ? *(npy_int16 *) ptr : *(npy_uint16 *) ptr;
+                }
+            }
+        }
+    } else if (bits_allocated == 32) {  // u4, i4
+        for (p = 0; p < samples_per_pixel; p++)
+        {
+            for (r = 0; r < rows; r++)
+            {
+                for (c = 0; c < columns; c++)
+                {
+                    ptr = PyArray_GETPTR3(arr, r, c, p);
+                    // `data[...]` is OPJ_INT32, so *may* support range i (1, 32), u (1, 31)
+                    image->comps[p].data[c + columns * r] = is_signed ? *(npy_int32 *) ptr : *(npy_uint32 *) ptr;
+                }
+            }
+        }
+    }
+    py_debug("Input image configured and populated with data");
+
+    /* Get an encoder handle */
+    switch (parameters.cod_format) {
+        case 0: {  // J2K codestream only
+            codec = opj_create_compress(OPJ_CODEC_J2K);
+            break;
+        }
+        case 2: { // JP2 codestream
+            codec = opj_create_compress(OPJ_CODEC_JP2);
+            break;
+        }
+        default:
+            py_error("Failed to set the encoding handler");
+            return_code = 22;
+            goto failure;
+    }
+
+    /* Send info, warning, error message to Python logging */
+    opj_set_info_handler(codec, info_callback, NULL);
+    opj_set_warning_handler(codec, warning_callback, NULL);
+    opj_set_error_handler(codec, error_callback, NULL);
+
+    if (! opj_setup_encoder(codec, &parameters, image)) {
+        py_error("Failed to set up the encoder");
+        return_code = 23;
+        goto failure;
+    }
+
+    // Creates an abstract output stream; allocates memory
+    stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE);
+
+    if (!stream) {
+        py_error("Failed to create the output stream");
+        return_code = 24;
+        goto failure;
+    }
+
+    // Functions for the stream
+    opj_stream_set_write_function(stream, py_write);
+    opj_stream_set_skip_function(stream, py_skip);
+    opj_stream_set_seek_function(stream, py_seek_set);
+    opj_stream_set_user_data(stream, dst, NULL);
+
+    OPJ_BOOL result;
+
+    // Encode `image` using `codec` and put the output in `stream`
+    py_debug("Encoding started");
+    result = opj_start_compress(codec, image, stream);
+    if (!result)  {
+        py_error("Failure result from 'opj_start_compress()'");
+        return_code = 25;
+        goto failure;
+    }
+
+    result = result && opj_encode(codec, stream);
+    if (!result)  {
+        py_error("Failure result from 'opj_encode()'");
+        return_code = 26;
+        goto failure;
+    }
+
+    result = result && opj_end_compress(codec, stream);
+    if (!result)  {
+        py_error("Failure result from 'opj_end_compress()'");
+        return_code = 27;
+        goto failure;
+    }
+
+    py_debug("Encoding completed");
+
+    opj_stream_destroy(stream);
+    opj_destroy_codec(codec);
+    opj_image_destroy(image);
+
+    return 0;
+
+    failure:
+        opj_stream_destroy(stream);
+        opj_destroy_codec(codec);
+        opj_image_destroy(image);
+        return return_code;
+}
diff --git a/lib/interface/utils.c b/lib/interface/utils.c
new file mode 100644
index 0000000..29517bc
--- /dev/null
+++ b/lib/interface/utils.c
@@ -0,0 +1,278 @@
+
+#include "Python.h"
+#include "utils.h"
+#include <../openjpeg/src/lib/openjp2/openjpeg.h>
+
+
+// Python logging
+void py_log(char *name, char *log_level, const char *log_msg)
+{
+    /* Log `log_msg` to the Python logger `name`.
+
+    Parameters
+    ----------
+    name
+        The name of the logger (i.e. logger.getLogger(name))
+    log_level
+        The log level to use DEBUG, INFO, WARNING, ERROR, CRITICAL
+    log_msg
+        The message to use.
+    */
+    static PyObject *module = NULL;
+    static PyObject *logger = NULL;
+    static PyObject *msg = NULL;
+    static PyObject *lib_name = NULL;
+
+    // import logging
+    module = PyImport_ImportModuleNoBlock("logging");
+    if (module == NULL) {
+        return;
+    }
+
+    // logger = logging.getLogger(lib_name)
+    lib_name = Py_BuildValue("s", name);
+    logger = PyObject_CallMethod(module, "getLogger", "O", lib_name);
+
+    // Create Python str and remove trailing newline
+    msg = Py_BuildValue("s", log_msg);
+    msg = PyObject_CallMethod(msg, "strip", NULL);
+
+    // logger.debug(msg), etc
+    if (strcmp(log_level, "DEBUG") == 0) {
+        PyObject_CallMethod(logger, "debug", "O", msg);
+    } else if (strcmp(log_level, "INFO") == 0) {
+        PyObject_CallMethod(logger, "info", "O", msg);
+    } else if (strcmp(log_level, "WARNING") == 0) {
+        PyObject_CallMethod(logger, "warning", "O", msg);
+    } else if (strcmp(log_level, "ERROR") == 0) {
+        PyObject_CallMethod(logger, "error", "O", msg);
+    } else if (strcmp(log_level, "CRITICAL") == 0) {
+        PyObject_CallMethod(logger, "critical", "O", msg);
+    }
+
+    Py_DECREF(msg);
+    Py_DECREF(lib_name);
+}
+
+// Python BinaryIO methods
+Py_ssize_t py_tell(PyObject *stream)
+{
+    /* Return the current position of the `stream`.
+
+    Parameters
+    ----------
+    stream : PyObject *
+        The Python stream object to use (must have a ``tell()`` method).
+
+    Returns
+    -------
+    Py_ssize_t
+        The new position in the `stream`.
+    */
+    PyObject *result;
+    Py_ssize_t location;
+
+    result = PyObject_CallMethod(stream, "tell", NULL);
+    location = PyLong_AsSsize_t(result);
+
+    Py_DECREF(result);
+
+    //printf("py_tell(): %u\n", location);
+    return location;
+}
+
+
+OPJ_SIZE_T py_read(void *destination, OPJ_SIZE_T nr_bytes, void *fd)
+{
+    /* Read `nr_bytes` from Python object `fd` and copy it to `destination`.
+
+    Parameters
+    ----------
+    destination : void *
+        The object where the read data will be copied.
+    nr_bytes : OPJ_SIZE_T
+        The number of bytes to be read.
+    fd : PyObject *
+        The Python file-like to read the data from (must have a ``read()``
+        method).
+
+    Returns
+    -------
+    OPJ_SIZE_T
+        The number of bytes read or -1 if reading failed or if trying to read
+        while at the end of the data.
+    */
+    PyObject* result;
+    char* buffer;
+    Py_ssize_t length;
+    int bytes_result;
+
+    // Py_ssize_t: signed int samed size as size_t
+    // fd.read(nr_bytes), "k" => C unsigned long int to Python int
+    result = PyObject_CallMethod(fd, "read", "n", nr_bytes);
+    // Returns the null-terminated contents of `result`
+    // `length` is Py_ssize_t *
+    // `buffer` is char **
+    // If `length` is NULL, returns -1
+    bytes_result = PyBytes_AsStringAndSize(result, &buffer, &length);
+
+    // `length` is NULL
+    if (bytes_result == -1)
+        goto error;
+
+    // More bytes read then asked for
+    if (length > (long)(nr_bytes))
+        goto error;
+
+    // Convert `length` to OPJ_SIZE_T - shouldn't have negative lengths
+    if (length < 0)
+        goto error;
+
+    OPJ_SIZE_T len_size_t = (OPJ_SIZE_T)(length);
+
+    // memcpy(void *dest, const void *src, size_t n)
+    memcpy(destination, buffer, len_size_t);
+
+    //printf("py_read(): %u bytes asked, %u bytes read\n", nr_bytes, len_size_t);
+
+    Py_DECREF(result);
+    return len_size_t ? len_size_t: (OPJ_SIZE_T)-1;
+
+error:
+    Py_DECREF(result);
+    return -1;
+}
+
+
+OPJ_BOOL py_seek(Py_ssize_t offset, void *stream, int whence)
+{
+    /* Change the `stream` position to the given `offset` from `whence`.
+
+    Parameters
+    ----------
+    offset : OPJ_OFF_T
+        The offset relative to `whence`.
+    stream : PyObject *
+        The Python stream object to seek (must have a ``seek()`` method).
+    whence : int
+        0 for SEEK_SET, 1 for SEEK_CUR, 2 for SEEK_END
+
+    Returns
+    -------
+    OPJ_TRUE : OBJ_BOOL
+    */
+    // Python and C; SEEK_SET is 0, SEEK_CUR is 1 and SEEK_END is 2
+    // fd.seek(nr_bytes),
+    // k: convert C unsigned long int to Python int
+    // i: convert C int to a Python integer
+    PyObject *result;
+    result = PyObject_CallMethod(stream, "seek", "ni", offset, whence);
+    Py_DECREF(result);
+
+    //printf("py_seek(): offset %u bytes from %u\n", offset, whence);
+
+    return OPJ_TRUE;
+}
+
+
+OPJ_BOOL py_seek_set(OPJ_OFF_T offset, void *stream)
+{
+    /* Change the `stream` position to the given `offset` from SEEK_SET.
+
+    Parameters
+    ----------
+    offset : OPJ_OFF_T
+        The offset relative to SEEK_SET.
+    stream : PyObject *
+        The Python stream object to seek (must have a ``seek()`` method).
+
+    Returns
+    -------
+    OPJ_TRUE : OBJ_BOOL
+    */
+    return py_seek(offset, stream, SEEK_SET);
+}
+
+
+OPJ_OFF_T py_skip(OPJ_OFF_T offset, void *stream)
+{
+    /* Change the `stream` position by `offset` from SEEK_CUR and return the
+    new position.
+
+    Parameters
+    ----------
+    offset : OPJ_OFF_T
+        The offset relative to SEEK_CUR.
+    stream : PyObject *
+        The Python stream object to seek (must have a ``seek()`` method).
+
+    Returns
+    -------
+    off_t
+        The new position in the `stream`.
+    */
+    py_seek(offset, stream, SEEK_CUR);
+
+    off_t pos;
+    pos = py_tell(stream);
+
+    return pos ? pos : (OPJ_OFF_T) -1;
+}
+
+
+OPJ_UINT64 py_length(PyObject * stream)
+{
+    /* Return the total length of the `stream`.
+
+    Parameters
+    ----------
+    stream : PyObject *
+        The Python stream object (must have ``seek()`` and ``tell()`` methods).
+
+    Returns
+    -------
+    OPJ_UINT64
+        The total length of the `stream`.
+    */
+    OPJ_OFF_T input_length = 0;
+
+    py_seek(0, stream, SEEK_END);
+    input_length = (OPJ_OFF_T)py_tell(stream);
+    py_seek(0, stream, SEEK_SET);
+
+    return (OPJ_UINT64)input_length;
+}
+
+
+OPJ_SIZE_T py_write(void *src, OPJ_SIZE_T nr_bytes, void *dst)
+{
+    /* Write `nr_bytes` from `src` to the Python object `dst`
+
+    Parameters
+    ----------
+    src : void *
+        The source of data.
+    nr_bytes : OPJ_SIZE_T
+        The length of the data to be written.
+    dst : void *
+        Where the data should be written to. Should be BinaryIO.
+
+    Returns
+    -------
+    OBJ_SIZE_T
+        The number of bytes written to dst.
+    */
+    PyObject *result;
+    PyObject *bytes_object;
+
+    // Create bytes object from `src` (of length `bytes`)
+    bytes_object = PyBytes_FromStringAndSize(src, nr_bytes);
+    // Use the bytes object to extend our dst using write(bytes_object)
+    // 'O' means pass the Python object untouched
+    result = PyObject_CallMethod(dst, "write", "O", bytes_object);
+
+    Py_DECREF(bytes_object);
+    Py_DECREF(result);
+
+    return nr_bytes;
+}
diff --git a/lib/interface/utils.h b/lib/interface/utils.h
new file mode 100644
index 0000000..9d417a6
--- /dev/null
+++ b/lib/interface/utils.h
@@ -0,0 +1,35 @@
+
+#include <../openjpeg/src/lib/openjp2/openjpeg.h>
+
+// Size of the buffer for the input/output streams
+#define BUFFER_SIZE OPJ_J2K_STREAM_CHUNK_SIZE
+
+#ifndef _PYOPJ_UTILS_H_
+#define _PYOPJ_UTILS_H_
+
+  // BinaryIO.tell()
+  extern Py_ssize_t py_tell(PyObject *stream);
+
+  // BinaryIO.read()
+  extern OPJ_SIZE_T py_read(void *destination, OPJ_SIZE_T nr_bytes, void *fd);
+
+  // BinaryIO.seek()
+  extern OPJ_BOOL py_seek(Py_ssize_t offset, void *stream, int whence);
+
+  // BinaryIO.seek(offset, SEEK_SET)
+  extern OPJ_BOOL py_seek_set(OPJ_OFF_T offset, void *stream);
+
+  // BinaryIO.seek(offset, SEEK_CUR)
+  extern OPJ_OFF_T py_skip(OPJ_OFF_T offset, void *stream);
+
+  // len(BinaryIO)
+  extern OPJ_UINT64 py_length(PyObject *stream);
+
+  // BinaryIO.write()
+  extern OPJ_SIZE_T py_write(void *src, OPJ_SIZE_T nr_bytes, void *dst);
+
+  // Log a message to the Python logger
+  extern void py_log(char *name, char *log_level, const char *log_msg);
+
+
+#endif
diff --git a/openjpeg/__init__.py b/openjpeg/__init__.py
index 58e52bf..4203502 100644
--- a/openjpeg/__init__.py
+++ b/openjpeg/__init__.py
@@ -1,4 +1,29 @@
 """Set package shortcuts."""
 
+import logging
+
 from ._version import __version__  # noqa: F401
-from .utils import decode, decode_pixel_data, get_parameters  # noqa: F401
+from .utils import (
+    decode,  # noqa: F401
+    decode_pixel_data,  # noqa: F401
+    encode,  # noqa: F401
+    encode_pixel_data,  # noqa: F401
+    get_parameters,  # noqa: F401
+)
+
+
+# Setup default logging
+_logger = logging.getLogger(__name__)
+_logger.addHandler(logging.NullHandler())
+_logger.debug(f"pylibjpeg v{__version__}")
+
+
+def debug_logger() -> None:
+    """Setup the logging for debugging."""
+    logger = logging.getLogger(__name__)
+    logger.handlers = []
+    handler = logging.StreamHandler()
+    logger.setLevel(logging.DEBUG)
+    formatter = logging.Formatter("%(levelname).1s: %(message)s")
+    handler.setFormatter(formatter)
+    logger.addHandler(handler)
diff --git a/openjpeg/_openjpeg.c b/openjpeg/_openjpeg.c
index 947383f..166ca9b 100644
--- a/openjpeg/_openjpeg.c
+++ b/openjpeg/_openjpeg.c
@@ -1,26 +1,28 @@
-/* Generated by Cython 3.0.7 */
+/* Generated by Cython 3.0.8 */
 
 /* BEGIN: Cython Metadata
 {
     "distutils": {
         "depends": [
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h",
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/arrayscalars.h",
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h",
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h",
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ufuncobject.h"
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h",
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/arrayscalars.h",
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h",
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h",
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include/numpy/ufuncobject.h"
         ],
         "include_dirs": [
             "/home/dean/Coding/src/pylibjpeg-openjpeg/lib/openjpeg/src/lib/openjp2",
             "/home/dean/Coding/src/pylibjpeg-openjpeg/lib/interface",
-            "/tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/core/include"
+            "/tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/core/include"
         ],
         "language": "c",
         "name": "_openjpeg",
         "sources": [
             "/home/dean/Coding/src/pylibjpeg-openjpeg/openjpeg/_openjpeg.pyx",
             "lib/interface/decode.c",
+            "lib/interface/encode.c",
             "lib/interface/color.c",
+            "lib/interface/utils.c",
             "lib/openjpeg/src/lib/openjp2/openjpeg.c",
             "lib/openjpeg/src/lib/openjp2/cio.c",
             "lib/openjpeg/src/lib/openjp2/sparse_array.c",
@@ -80,10 +82,10 @@ END: Cython Metadata */
 #else
 #define __PYX_EXTRA_ABI_MODULE_NAME ""
 #endif
-#define CYTHON_ABI "3_0_7" __PYX_EXTRA_ABI_MODULE_NAME
+#define CYTHON_ABI "3_0_8" __PYX_EXTRA_ABI_MODULE_NAME
 #define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI
 #define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "."
-#define CYTHON_HEX_VERSION 0x030007F0
+#define CYTHON_HEX_VERSION 0x030008F0
 #define CYTHON_FUTURE_DIVISION 1
 #include <stddef.h>
 #ifndef offsetof
@@ -625,14 +627,14 @@ END: Cython Metadata */
         PyObject *exception_table = NULL;
         PyObject *types_module=NULL, *code_type=NULL, *result=NULL;
         #if __PYX_LIMITED_VERSION_HEX < 0x030B0000
-        PyObject *version_info; // borrowed
+        PyObject *version_info;
         PyObject *py_minor_version = NULL;
         #endif
         long minor_version = 0;
         PyObject *type, *value, *traceback;
         PyErr_Fetch(&type, &value, &traceback);
         #if __PYX_LIMITED_VERSION_HEX >= 0x030B0000
-        minor_version = 11; // we don't yet need to distinguish between versions > 11
+        minor_version = 11;
         #else
         if (!(version_info = PySys_GetObject("version_info"))) goto end;
         if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end;
@@ -690,7 +692,7 @@ END: Cython Metadata */
                                                     PyObject *fv, PyObject *cell, PyObject* fn,
                                                     PyObject *name, int fline, PyObject *lnos) {
     PyCodeObject *result;
-    PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0);  // we don't have access to __pyx_empty_bytes here
+    PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0);
     if (!empty_bytes) return NULL;
     result =
       #if PY_VERSION_HEX >= 0x030C0000
@@ -1397,7 +1399,7 @@ static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
   #endif
   typedef Py_ssize_t  __Pyx_compact_pylong;
   typedef size_t  __Pyx_compact_upylong;
-  #else  // Py < 3.12
+  #else
   #define __Pyx_PyLong_IsNeg(x)  (Py_SIZE(x) < 0)
   #define __Pyx_PyLong_IsNonNeg(x)  (Py_SIZE(x) >= 0)
   #define __Pyx_PyLong_IsZero(x)  (Py_SIZE(x) == 0)
@@ -1550,7 +1552,7 @@ static const char *__pyx_f[] = {
 
 /* #### Code section: numeric_typedefs ### */
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":731
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":731
  * # in Cython to enable them only on the right systems.
  * 
  * ctypedef npy_int8       int8_t             # <<<<<<<<<<<<<<
@@ -1559,7 +1561,7 @@ static const char *__pyx_f[] = {
  */
 typedef npy_int8 __pyx_t_5numpy_int8_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":732
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":732
  * 
  * ctypedef npy_int8       int8_t
  * ctypedef npy_int16      int16_t             # <<<<<<<<<<<<<<
@@ -1568,7 +1570,7 @@ typedef npy_int8 __pyx_t_5numpy_int8_t;
  */
 typedef npy_int16 __pyx_t_5numpy_int16_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":733
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":733
  * ctypedef npy_int8       int8_t
  * ctypedef npy_int16      int16_t
  * ctypedef npy_int32      int32_t             # <<<<<<<<<<<<<<
@@ -1577,7 +1579,7 @@ typedef npy_int16 __pyx_t_5numpy_int16_t;
  */
 typedef npy_int32 __pyx_t_5numpy_int32_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":734
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":734
  * ctypedef npy_int16      int16_t
  * ctypedef npy_int32      int32_t
  * ctypedef npy_int64      int64_t             # <<<<<<<<<<<<<<
@@ -1586,7 +1588,7 @@ typedef npy_int32 __pyx_t_5numpy_int32_t;
  */
 typedef npy_int64 __pyx_t_5numpy_int64_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":738
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":738
  * #ctypedef npy_int128     int128_t
  * 
  * ctypedef npy_uint8      uint8_t             # <<<<<<<<<<<<<<
@@ -1595,7 +1597,7 @@ typedef npy_int64 __pyx_t_5numpy_int64_t;
  */
 typedef npy_uint8 __pyx_t_5numpy_uint8_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":739
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":739
  * 
  * ctypedef npy_uint8      uint8_t
  * ctypedef npy_uint16     uint16_t             # <<<<<<<<<<<<<<
@@ -1604,7 +1606,7 @@ typedef npy_uint8 __pyx_t_5numpy_uint8_t;
  */
 typedef npy_uint16 __pyx_t_5numpy_uint16_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":740
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":740
  * ctypedef npy_uint8      uint8_t
  * ctypedef npy_uint16     uint16_t
  * ctypedef npy_uint32     uint32_t             # <<<<<<<<<<<<<<
@@ -1613,7 +1615,7 @@ typedef npy_uint16 __pyx_t_5numpy_uint16_t;
  */
 typedef npy_uint32 __pyx_t_5numpy_uint32_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":741
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":741
  * ctypedef npy_uint16     uint16_t
  * ctypedef npy_uint32     uint32_t
  * ctypedef npy_uint64     uint64_t             # <<<<<<<<<<<<<<
@@ -1622,7 +1624,7 @@ typedef npy_uint32 __pyx_t_5numpy_uint32_t;
  */
 typedef npy_uint64 __pyx_t_5numpy_uint64_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":745
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":745
  * #ctypedef npy_uint128    uint128_t
  * 
  * ctypedef npy_float32    float32_t             # <<<<<<<<<<<<<<
@@ -1631,7 +1633,7 @@ typedef npy_uint64 __pyx_t_5numpy_uint64_t;
  */
 typedef npy_float32 __pyx_t_5numpy_float32_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":746
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":746
  * 
  * ctypedef npy_float32    float32_t
  * ctypedef npy_float64    float64_t             # <<<<<<<<<<<<<<
@@ -1640,7 +1642,7 @@ typedef npy_float32 __pyx_t_5numpy_float32_t;
  */
 typedef npy_float64 __pyx_t_5numpy_float64_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":755
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":755
  * # The int types are mapped a bit surprising --
  * # numpy.int corresponds to 'l' and numpy.long to 'q'
  * ctypedef npy_long       int_t             # <<<<<<<<<<<<<<
@@ -1649,7 +1651,7 @@ typedef npy_float64 __pyx_t_5numpy_float64_t;
  */
 typedef npy_long __pyx_t_5numpy_int_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":756
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":756
  * # numpy.int corresponds to 'l' and numpy.long to 'q'
  * ctypedef npy_long       int_t
  * ctypedef npy_longlong   long_t             # <<<<<<<<<<<<<<
@@ -1658,7 +1660,7 @@ typedef npy_long __pyx_t_5numpy_int_t;
  */
 typedef npy_longlong __pyx_t_5numpy_long_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":757
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":757
  * ctypedef npy_long       int_t
  * ctypedef npy_longlong   long_t
  * ctypedef npy_longlong   longlong_t             # <<<<<<<<<<<<<<
@@ -1667,7 +1669,7 @@ typedef npy_longlong __pyx_t_5numpy_long_t;
  */
 typedef npy_longlong __pyx_t_5numpy_longlong_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":759
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":759
  * ctypedef npy_longlong   longlong_t
  * 
  * ctypedef npy_ulong      uint_t             # <<<<<<<<<<<<<<
@@ -1676,7 +1678,7 @@ typedef npy_longlong __pyx_t_5numpy_longlong_t;
  */
 typedef npy_ulong __pyx_t_5numpy_uint_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":760
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":760
  * 
  * ctypedef npy_ulong      uint_t
  * ctypedef npy_ulonglong  ulong_t             # <<<<<<<<<<<<<<
@@ -1685,7 +1687,7 @@ typedef npy_ulong __pyx_t_5numpy_uint_t;
  */
 typedef npy_ulonglong __pyx_t_5numpy_ulong_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":761
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":761
  * ctypedef npy_ulong      uint_t
  * ctypedef npy_ulonglong  ulong_t
  * ctypedef npy_ulonglong  ulonglong_t             # <<<<<<<<<<<<<<
@@ -1694,7 +1696,7 @@ typedef npy_ulonglong __pyx_t_5numpy_ulong_t;
  */
 typedef npy_ulonglong __pyx_t_5numpy_ulonglong_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":763
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":763
  * ctypedef npy_ulonglong  ulonglong_t
  * 
  * ctypedef npy_intp       intp_t             # <<<<<<<<<<<<<<
@@ -1703,7 +1705,7 @@ typedef npy_ulonglong __pyx_t_5numpy_ulonglong_t;
  */
 typedef npy_intp __pyx_t_5numpy_intp_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":764
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":764
  * 
  * ctypedef npy_intp       intp_t
  * ctypedef npy_uintp      uintp_t             # <<<<<<<<<<<<<<
@@ -1712,7 +1714,7 @@ typedef npy_intp __pyx_t_5numpy_intp_t;
  */
 typedef npy_uintp __pyx_t_5numpy_uintp_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":766
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":766
  * ctypedef npy_uintp      uintp_t
  * 
  * ctypedef npy_double     float_t             # <<<<<<<<<<<<<<
@@ -1721,7 +1723,7 @@ typedef npy_uintp __pyx_t_5numpy_uintp_t;
  */
 typedef npy_double __pyx_t_5numpy_float_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":767
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":767
  * 
  * ctypedef npy_double     float_t
  * ctypedef npy_double     double_t             # <<<<<<<<<<<<<<
@@ -1730,7 +1732,7 @@ typedef npy_double __pyx_t_5numpy_float_t;
  */
 typedef npy_double __pyx_t_5numpy_double_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":768
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":768
  * ctypedef npy_double     float_t
  * ctypedef npy_double     double_t
  * ctypedef npy_longdouble longdouble_t             # <<<<<<<<<<<<<<
@@ -1767,7 +1769,7 @@ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(do
 
 /*--- Type declarations ---*/
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":770
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":770
  * ctypedef npy_longdouble longdouble_t
  * 
  * ctypedef npy_cfloat      cfloat_t             # <<<<<<<<<<<<<<
@@ -1776,7 +1778,7 @@ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(do
  */
 typedef npy_cfloat __pyx_t_5numpy_cfloat_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":771
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":771
  * 
  * ctypedef npy_cfloat      cfloat_t
  * ctypedef npy_cdouble     cdouble_t             # <<<<<<<<<<<<<<
@@ -1785,7 +1787,7 @@ typedef npy_cfloat __pyx_t_5numpy_cfloat_t;
  */
 typedef npy_cdouble __pyx_t_5numpy_cdouble_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":772
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":772
  * ctypedef npy_cfloat      cfloat_t
  * ctypedef npy_cdouble     cdouble_t
  * ctypedef npy_clongdouble clongdouble_t             # <<<<<<<<<<<<<<
@@ -1794,7 +1796,7 @@ typedef npy_cdouble __pyx_t_5numpy_cdouble_t;
  */
 typedef npy_clongdouble __pyx_t_5numpy_clongdouble_t;
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":774
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":774
  * ctypedef npy_clongdouble clongdouble_t
  * 
  * ctypedef npy_cdouble     complex_t             # <<<<<<<<<<<<<<
@@ -1808,8 +1810,8 @@ typedef struct __pyx_defaults __pyx_defaults;
 struct __pyx_defaults1;
 typedef struct __pyx_defaults1 __pyx_defaults1;
 
-/* "_openjpeg.pyx":12
- * cimport numpy as np
+/* "_openjpeg.pyx":14
+ * cimport numpy as cnp
  * 
  * cdef extern struct JPEG2000Parameters:             # <<<<<<<<<<<<<<
  *     uint32_t columns
@@ -2037,8 +2039,8 @@ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int
     #define __Pyx_Arg_NewRef_VARARGS(arg) __Pyx_NewRef(arg)
     #define __Pyx_Arg_XDECREF_VARARGS(arg) Py_XDECREF(arg)
 #else
-    #define __Pyx_Arg_NewRef_VARARGS(arg) arg // no-op
-    #define __Pyx_Arg_XDECREF_VARARGS(arg) // no-op - arg is borrowed
+    #define __Pyx_Arg_NewRef_VARARGS(arg) arg
+    #define __Pyx_Arg_XDECREF_VARARGS(arg)
 #endif
 #define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds)
 #define __Pyx_KwValues_VARARGS(args, nargs) NULL
@@ -2054,8 +2056,9 @@ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int
   #else
     #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw)
   #endif
-    #define __Pyx_Arg_NewRef_FASTCALL(arg) arg // no-op, __Pyx_Arg_FASTCALL is direct and this needs
-    #define __Pyx_Arg_XDECREF_FASTCALL(arg)  // no-op - arg was returned from array
+    #define __Pyx_Arg_NewRef_FASTCALL(arg) arg  /* no-op, __Pyx_Arg_FASTCALL is direct and this needs
+                                                   to have the same reference counting */
+    #define __Pyx_Arg_XDECREF_FASTCALL(arg)
 #else
     #define __Pyx_Arg_FASTCALL __Pyx_Arg_VARARGS
     #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS
@@ -2202,34 +2205,15 @@ static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key);
 #define __Pyx_PyObject_Dict_GetItem(obj, name)  PyObject_GetItem(obj, name)
 #endif
 
+/* PyIntCompare.proto */
+static CYTHON_INLINE int __Pyx_PyInt_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace);
+
 /* ExtTypeTest.proto */
 static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type);
 
 /* PyObjectCallOneArg.proto */
 static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg);
 
-/* PyObjectFormatSimple.proto */
-#if CYTHON_COMPILING_IN_PYPY
-    #define __Pyx_PyObject_FormatSimple(s, f) (\
-        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
-        PyObject_Format(s, f))
-#elif PY_MAJOR_VERSION < 3
-    #define __Pyx_PyObject_FormatSimple(s, f) (\
-        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
-        likely(PyString_CheckExact(s)) ? PyUnicode_FromEncodedObject(s, NULL, "strict") :\
-        PyObject_Format(s, f))
-#elif CYTHON_USE_TYPE_SLOTS
-    #define __Pyx_PyObject_FormatSimple(s, f) (\
-        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
-        likely(PyLong_CheckExact(s)) ? PyLong_Type.tp_repr(s) :\
-        likely(PyFloat_CheckExact(s)) ? PyFloat_Type.tp_repr(s) :\
-        PyObject_Format(s, f))
-#else
-    #define __Pyx_PyObject_FormatSimple(s, f) (\
-        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
-        PyObject_Format(s, f))
-#endif
-
 /* GetItemInt.proto */
 #define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck)\
     (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
@@ -2252,26 +2236,262 @@ static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j);
 static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
                                                      int is_list, int wraparound, int boundscheck);
 
+/* PyObjectFormatSimple.proto */
+#if CYTHON_COMPILING_IN_PYPY
+    #define __Pyx_PyObject_FormatSimple(s, f) (\
+        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
+        PyObject_Format(s, f))
+#elif PY_MAJOR_VERSION < 3
+    #define __Pyx_PyObject_FormatSimple(s, f) (\
+        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
+        likely(PyString_CheckExact(s)) ? PyUnicode_FromEncodedObject(s, NULL, "strict") :\
+        PyObject_Format(s, f))
+#elif CYTHON_USE_TYPE_SLOTS
+    #define __Pyx_PyObject_FormatSimple(s, f) (\
+        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
+        likely(PyLong_CheckExact(s)) ? PyLong_Type.tp_repr(s) :\
+        likely(PyFloat_CheckExact(s)) ? PyFloat_Type.tp_repr(s) :\
+        PyObject_Format(s, f))
+#else
+    #define __Pyx_PyObject_FormatSimple(s, f) (\
+        likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) :\
+        PyObject_Format(s, f))
+#endif
+
 /* RaiseUnboundLocalError.proto */
 static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
 
+/* PyIntBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_PyInt_MultiplyObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyInt_MultiplyObjC(op1, op2, intval, inplace, zerodivision_check)\
+    (inplace ? PyNumber_InPlaceMultiply(op1, op2) : PyNumber_Multiply(op1, op2))
+#endif
+
+/* JoinPyUnicode.proto */
+static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_count, Py_ssize_t result_ulength,
+                                      Py_UCS4 max_char);
+
+/* PySequenceContains.proto */
+static CYTHON_INLINE int __Pyx_PySequence_ContainsTF(PyObject* item, PyObject* seq, int eq) {
+    int result = PySequence_Contains(seq, item);
+    return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
+}
+
+/* GCCDiagnostics.proto */
+#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define __Pyx_HAS_GCC_DIAGNOSTIC
+#endif
+
+/* BuildPyUnicode.proto */
+static PyObject* __Pyx_PyUnicode_BuildFromAscii(Py_ssize_t ulength, char* chars, int clength,
+                                                int prepend_sign, char padding_char);
+
+/* CIntToPyUnicode.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_From_int(int value, Py_ssize_t width, char padding_char, char format_char);
+
+/* ListCompAppend.proto */
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS
+static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) {
+    PyListObject* L = (PyListObject*) list;
+    Py_ssize_t len = Py_SIZE(list);
+    if (likely(L->allocated > len)) {
+        Py_INCREF(x);
+        #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
+        L->ob_item[len] = x;
+        #else
+        PyList_SET_ITEM(list, len, x);
+        #endif
+        __Pyx_SET_SIZE(list, len + 1);
+        return 0;
+    }
+    return PyList_Append(list, x);
+}
+#else
+#define __Pyx_ListComp_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* pybytes_as_double.proto */
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj);
+static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length);
+static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) {
+    char* as_c_string;
+    Py_ssize_t size;
+#if CYTHON_ASSUME_SAFE_MACROS
+    as_c_string = PyBytes_AS_STRING(obj);
+    size = PyBytes_GET_SIZE(obj);
+#else
+    if (PyBytes_AsStringAndSize(obj, &as_c_string, &size) < 0) {
+        return (double)-1;
+    }
+#endif
+    return __Pyx__PyBytes_AsDouble(obj, as_c_string, size);
+}
+static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) {
+    char* as_c_string;
+    Py_ssize_t size;
+#if CYTHON_ASSUME_SAFE_MACROS
+    as_c_string = PyByteArray_AS_STRING(obj);
+    size = PyByteArray_GET_SIZE(obj);
+#else
+    as_c_string = PyByteArray_AsString(obj);
+    if (as_c_string == NULL) {
+        return (double)-1;
+    }
+    size = PyByteArray_Size(obj);
+#endif
+    return __Pyx__PyBytes_AsDouble(obj, as_c_string, size);
+}
+
+/* pyunicode_as_double.proto */
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS
+static const char* __Pyx__PyUnicode_AsDouble_Copy(const void* data, const int kind, char* buffer, Py_ssize_t start, Py_ssize_t end) {
+    int last_was_punctuation;
+    Py_ssize_t i;
+    last_was_punctuation = 1;
+    for (i=start; i <= end; i++) {
+        Py_UCS4 chr = PyUnicode_READ(kind, data, i);
+        int is_punctuation = (chr == '_') | (chr == '.');
+        *buffer = (char)chr;
+        buffer += (chr != '_');
+        if (unlikely(chr > 127)) goto parse_failure;
+        if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure;
+        last_was_punctuation = is_punctuation;
+    }
+    if (unlikely(last_was_punctuation)) goto parse_failure;
+    *buffer = '\0';
+    return buffer;
+parse_failure:
+    return NULL;
+}
+static double __Pyx__PyUnicode_AsDouble_inf_nan(const void* data, int kind, Py_ssize_t start, Py_ssize_t length) {
+    int matches = 1;
+    Py_UCS4 chr;
+    Py_UCS4 sign = PyUnicode_READ(kind, data, start);
+    int is_signed = (sign == '-') | (sign == '+');
+    start += is_signed;
+    length -= is_signed;
+    switch (PyUnicode_READ(kind, data, start)) {
+        #ifdef Py_NAN
+        case 'n':
+        case 'N':
+            if (unlikely(length != 3)) goto parse_failure;
+            chr = PyUnicode_READ(kind, data, start+1);
+            matches &= (chr == 'a') | (chr == 'A');
+            chr = PyUnicode_READ(kind, data, start+2);
+            matches &= (chr == 'n') | (chr == 'N');
+            if (unlikely(!matches)) goto parse_failure;
+            return (sign == '-') ? -Py_NAN : Py_NAN;
+        #endif
+        case 'i':
+        case 'I':
+            if (unlikely(length < 3)) goto parse_failure;
+            chr = PyUnicode_READ(kind, data, start+1);
+            matches &= (chr == 'n') | (chr == 'N');
+            chr = PyUnicode_READ(kind, data, start+2);
+            matches &= (chr == 'f') | (chr == 'F');
+            if (likely(length == 3 && matches))
+                return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+            if (unlikely(length != 8)) goto parse_failure;
+            chr = PyUnicode_READ(kind, data, start+3);
+            matches &= (chr == 'i') | (chr == 'I');
+            chr = PyUnicode_READ(kind, data, start+4);
+            matches &= (chr == 'n') | (chr == 'N');
+            chr = PyUnicode_READ(kind, data, start+5);
+            matches &= (chr == 'i') | (chr == 'I');
+            chr = PyUnicode_READ(kind, data, start+6);
+            matches &= (chr == 't') | (chr == 'T');
+            chr = PyUnicode_READ(kind, data, start+7);
+            matches &= (chr == 'y') | (chr == 'Y');
+            if (unlikely(!matches)) goto parse_failure;
+            return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+        case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+            break;
+        default:
+            goto parse_failure;
+    }
+    return 0.0;
+parse_failure:
+    return -1.0;
+}
+static double __Pyx_PyUnicode_AsDouble_WithSpaces(PyObject *obj) {
+    double value;
+    const char *last;
+    char *end;
+    Py_ssize_t start, length = PyUnicode_GET_LENGTH(obj);
+    const int kind = PyUnicode_KIND(obj);
+    const void* data = PyUnicode_DATA(obj);
+    start = 0;
+    while (Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, start)))
+        start++;
+    while (start < length - 1 && Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, length - 1)))
+        length--;
+    length -= start;
+    if (unlikely(length <= 0)) goto fallback;
+    value = __Pyx__PyUnicode_AsDouble_inf_nan(data, kind, start, length);
+    if (unlikely(value == -1.0)) goto fallback;
+    if (value != 0.0) return value;
+    if (length < 40) {
+        char number[40];
+        last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+        if (unlikely(!last)) goto fallback;
+        value = PyOS_string_to_double(number, &end, NULL);
+    } else {
+        char *number = (char*) PyMem_Malloc((length + 1) * sizeof(char));
+        if (unlikely(!number)) goto fallback;
+        last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+        if (unlikely(!last)) {
+            PyMem_Free(number);
+            goto fallback;
+        }
+        value = PyOS_string_to_double(number, &end, NULL);
+        PyMem_Free(number);
+    }
+    if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+        return value;
+    }
+fallback:
+    return __Pyx_SlowPyString_AsDouble(obj);
+}
+#endif
+static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) {
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS
+    if (unlikely(__Pyx_PyUnicode_READY(obj) == -1))
+        return (double)-1;
+    if (likely(PyUnicode_IS_ASCII(obj))) {
+        const char *s;
+        Py_ssize_t length;
+        s = PyUnicode_AsUTF8AndSize(obj, &length);
+        return __Pyx__PyBytes_AsDouble(obj, s, length);
+    }
+    return __Pyx_PyUnicode_AsDouble_WithSpaces(obj);
+#else
+    return __Pyx_SlowPyString_AsDouble(obj);
+#endif
+}
+
+/* pynumber_float.proto */
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj);
+#define __Pyx_PyNumber_Float(x) (PyFloat_CheckExact(x) ? __Pyx_NewRef(x) : __Pyx__PyNumber_Float(x))
+
 /* TypeImport.proto */
-#ifndef __PYX_HAVE_RT_ImportType_proto_3_0_7
-#define __PYX_HAVE_RT_ImportType_proto_3_0_7
+#ifndef __PYX_HAVE_RT_ImportType_proto_3_0_8
+#define __PYX_HAVE_RT_ImportType_proto_3_0_8
 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 #include <stdalign.h>
 #endif
 #if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || __cplusplus >= 201103L
-#define __PYX_GET_STRUCT_ALIGNMENT_3_0_7(s) alignof(s)
+#define __PYX_GET_STRUCT_ALIGNMENT_3_0_8(s) alignof(s)
 #else
-#define __PYX_GET_STRUCT_ALIGNMENT_3_0_7(s) sizeof(void*)
+#define __PYX_GET_STRUCT_ALIGNMENT_3_0_8(s) sizeof(void*)
 #endif
-enum __Pyx_ImportType_CheckSize_3_0_7 {
-   __Pyx_ImportType_CheckSize_Error_3_0_7 = 0,
-   __Pyx_ImportType_CheckSize_Warn_3_0_7 = 1,
-   __Pyx_ImportType_CheckSize_Ignore_3_0_7 = 2
+enum __Pyx_ImportType_CheckSize_3_0_8 {
+   __Pyx_ImportType_CheckSize_Error_3_0_8 = 0,
+   __Pyx_ImportType_CheckSize_Warn_3_0_8 = 1,
+   __Pyx_ImportType_CheckSize_Ignore_3_0_8 = 2
 };
-static PyTypeObject *__Pyx_ImportType_3_0_7(PyObject* module, const char *module_name, const char *class_name, size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_7 check_size);
+static PyTypeObject *__Pyx_ImportType_3_0_8(PyObject* module, const char *module_name, const char *class_name, size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_8 check_size);
 #endif
 
 /* Import.proto */
@@ -2384,7 +2604,7 @@ typedef struct {
 #endif
     void *defaults;
     int defaults_pyobjects;
-    size_t defaults_size;  // used by FusedFunction for copying defaults
+    size_t defaults_size;
     int flags;
     PyObject *defaults_tuple;
     PyObject *defaults_kwdict;
@@ -2465,11 +2685,6 @@ static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
 static void __Pyx_AddTraceback(const char *funcname, int c_line,
                                int py_line, const char *filename);
 
-/* GCCDiagnostics.proto */
-#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-#define __Pyx_HAS_GCC_DIAGNOSTIC
-#endif
-
 /* RealImag.proto */
 #if CYTHON_CCOMPLEX
   #ifdef __cplusplus
@@ -2656,6 +2871,7 @@ static CYTHON_INLINE char *__pyx_f_5numpy_7ndarray_4data_data(PyArrayObject *__p
 __PYX_EXTERN_C DL_IMPORT(char) *OpenJpegVersion(void); /*proto*/
 __PYX_EXTERN_C DL_IMPORT(int) Decode(void *, unsigned char *, int); /*proto*/
 __PYX_EXTERN_C DL_IMPORT(int) GetParameters(void *, int, struct __pyx_t_9_openjpeg_JPEG2000Parameters *); /*proto*/
+__PYX_EXTERN_C DL_IMPORT(int) Encode(PyArrayObject *, PyObject *, int, int, int, PyObject *, PyObject *, int); /*proto*/
 /* #### Code section: typeinfo ### */
 /* #### Code section: before_global_var ### */
 #define __Pyx_MODULE_NAME "_openjpeg"
@@ -2664,27 +2880,37 @@ int __pyx_module_is_main__openjpeg = 0;
 
 /* Implementation of "_openjpeg" */
 /* #### Code section: global_var ### */
-static PyObject *__pyx_builtin_RuntimeError;
 static PyObject *__pyx_builtin_KeyError;
+static PyObject *__pyx_builtin_RuntimeError;
+static PyObject *__pyx_builtin_ValueError;
 static PyObject *__pyx_builtin_ImportError;
 /* #### Code section: string_decls ### */
+static const char __pyx_k_x[] = "x";
 static const char __pyx_k__3[] = ": ";
-static const char __pyx_k__4[] = ".";
-static const char __pyx_k__5[] = "*";
+static const char __pyx_k__4[] = ")";
+static const char __pyx_k__6[] = ", ";
 static const char __pyx_k_fp[] = "fp";
+static const char __pyx_k_io[] = "io";
 static const char __pyx_k_np[] = "np";
 static const char __pyx_k_YUV[] = "YUV";
-static const char __pyx_k__12[] = "?";
+static const char __pyx_k__10[] = ".";
+static const char __pyx_k__11[] = "*";
+static const char __pyx_k__20[] = "?";
+static const char __pyx_k_arr[] = "arr";
 static const char __pyx_k_bpp[] = "bpp";
-static const char __pyx_k_get[] = "get";
+static const char __pyx_k_dst[] = "dst";
 static const char __pyx_k_int[] = "int";
+static const char __pyx_k_max[] = "max";
+static const char __pyx_k_min[] = "min";
 static const char __pyx_k_msg[] = "msg";
 static const char __pyx_k_out[] = "out";
 static const char __pyx_k_ptr[] = "ptr";
 static const char __pyx_k_CYMK[] = "CYMK";
 static const char __pyx_k_Dict[] = "Dict";
+static const char __pyx_k_List[] = "List";
 static const char __pyx_k_bool[] = "bool";
 static const char __pyx_k_ceil[] = "ceil";
+static const char __pyx_k_int8[] = "int8";
 static const char __pyx_k_main[] = "__main__";
 static const char __pyx_k_math[] = "math";
 static const char __pyx_k_name[] = "__name__";
@@ -2693,58 +2919,90 @@ static const char __pyx_k_rows[] = "rows";
 static const char __pyx_k_sRGB[] = "sRGB";
 static const char __pyx_k_spec[] = "__spec__";
 static const char __pyx_k_test[] = "__test__";
+static const char __pyx_k_Tuple[] = "Tuple";
 static const char __pyx_k_Union[] = "Union";
 static const char __pyx_k_bytes[] = "bytes";
 static const char __pyx_k_codec[] = "codec";
 static const char __pyx_k_dtype[] = "dtype";
 static const char __pyx_k_e_YCC[] = "e-YCC";
+static const char __pyx_k_int16[] = "int16";
+static const char __pyx_k_int32[] = "int32";
 static const char __pyx_k_numpy[] = "numpy";
 static const char __pyx_k_p_out[] = "p_out";
 static const char __pyx_k_param[] = "param";
 static const char __pyx_k_uint8[] = "uint8";
 static const char __pyx_k_zeros[] = "zeros";
 static const char __pyx_k_ERRORS[] = "ERRORS";
+static const char __pyx_k_LOGGER[] = "LOGGER";
 static const char __pyx_k_decode[] = "decode";
+static const char __pyx_k_encode[] = "encode";
 static const char __pyx_k_import[] = "__import__";
 static const char __pyx_k_result[] = "result";
 static const char __pyx_k_return[] = "return";
 static const char __pyx_k_typing[] = "typing";
+static const char __pyx_k_uint16[] = "uint16";
+static const char __pyx_k_uint32[] = "uint32";
+static const char __pyx_k_BytesIO[] = "BytesIO";
+static const char __pyx_k_allowed[] = "allowed";
+static const char __pyx_k_arr_max[] = "arr_max";
+static const char __pyx_k_arr_min[] = "arr_min";
 static const char __pyx_k_colours[] = "colours";
 static const char __pyx_k_columns[] = "columns";
+static const char __pyx_k_logging[] = "logging";
 static const char __pyx_k_p_param[] = "p_param";
 static const char __pyx_k_unknown[] = "unknown";
+static const char __pyx_k_use_mct[] = "use_mct";
 static const char __pyx_k_version[] = "version";
 static const char __pyx_k_BinaryIO[] = "BinaryIO";
 static const char __pyx_k_KeyError[] = "KeyError";
 static const char __pyx_k_as_array[] = "as_array";
+static const char __pyx_k_getvalue[] = "getvalue";
+static const char __pyx_k_itemsize[] = "itemsize";
 static const char __pyx_k_nr_bytes[] = "nr_bytes";
 static const char __pyx_k_nr_tiles[] = "nr_tiles";
 static const char __pyx_k_openjpeg[] = "_openjpeg";
+static const char __pyx_k_getLogger[] = "getLogger";
 static const char __pyx_k_is_signed[] = "is_signed";
 static const char __pyx_k_precision[] = "precision";
+static const char __pyx_k_ValueError[] = "ValueError";
 static const char __pyx_k_monochrome[] = "monochrome";
 static const char __pyx_k_parameters[] = "parameters";
 static const char __pyx_k_ImportError[] = "ImportError";
+static const char __pyx_k_bits_stored[] = "bits_stored";
 static const char __pyx_k_colourspace[] = "colourspace";
 static const char __pyx_k_get_version[] = "get_version";
+static const char __pyx_k_return_code[] = "return_code";
 static const char __pyx_k_unspecified[] = "unspecified";
 static const char __pyx_k_RuntimeError[] = "RuntimeError";
+static const char __pyx_k_codec_format[] = "codec_format";
 static const char __pyx_k_initializing[] = "_initializing";
 static const char __pyx_k_is_coroutine[] = "_is_coroutine";
 static const char __pyx_k_nr_components[] = "nr_components";
 static const char __pyx_k_get_parameters[] = "get_parameters";
+static const char __pyx_k_Tuple_int_bytes[] = "Tuple[int, bytes]";
 static const char __pyx_k_asyncio_coroutines[] = "asyncio.coroutines";
 static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback";
+static const char __pyx_k_compression_ratios[] = "compression_ratios";
+static const char __pyx_k_signal_noise_ratios[] = "signal_noise_ratios";
+static const char __pyx_k_A_bits_stored_value_of[] = "A 'bits_stored' value of ";
 static const char __pyx_k_failed_to_decode_image[] = "failed to decode image";
 static const char __pyx_k_openjpeg__openjpeg_pyx[] = "openjpeg/_openjpeg.pyx";
 static const char __pyx_k_failed_to_read_the_header[] = "failed to read the header";
 static const char __pyx_k_Union_np_ndarray_bytearray[] = "Union[np.ndarray, bytearray]";
+static const char __pyx_k_photometric_interpretation[] = "photometric_interpretation";
 static const char __pyx_k_Dict_str_Union_str_int_bool[] = "Dict[str, Union[str, int, bool]]";
-static const char __pyx_k_Error_decoding_the_J2K_data[] = "Error decoding the J2K data: ";
+static const char __pyx_k_Error_decoding_the_J2K_data[] = "Error decoding the J2K data";
 static const char __pyx_k_failed_to_setup_the_decoder[] = "failed to setup the decoder";
-static const char __pyx_k_Error_decoding_the_J2K_data_2[] = "Error decoding the J2K data";
 static const char __pyx_k_failed_to_set_the_decoded_area[] = "failed to set the decoded area";
+static const char __pyx_k_is_incompatible_with_the_range[] = " is incompatible with the range of pixel data in the input array: (";
+static const char __pyx_k_More_than_10_compression_layers[] = "More than 10 compression layers is not supported";
+static const char __pyx_k_The_input_array_contains_values[] = "The input array contains values outside the range of the maximum supported bit-depth of 24";
 static const char __pyx_k_numpy_core_multiarray_failed_to[] = "numpy.core.multiarray failed to import";
+static const char __pyx_k_only_bool_u1_u2_u4_i1_i2_and_i4[] = "', only bool, u1, u2, u4, i1, i2 and i4 are supported";
+static const char __pyx_k_Invalid_value_for_the_bits_store[] = "Invalid value for the 'bits_stored' parameter, the value must be in the range (1, ";
+static const char __pyx_k_Only_one_of_compression_ratios_o[] = "Only one of 'compression_ratios' or 'signal_noise_ratios' is allowed when performing lossy compression";
+static const char __pyx_k_The_input_array_has_an_unsupport[] = "The input array has an unsupported dtype '";
+static const char __pyx_k_The_value_of_the_codec_format_pa[] = "The value of the 'codec_format' parameter is invalid, must be 0 or 2";
 static const char __pyx_k_failed_to_create_the_input_strea[] = "failed to create the input stream";
 static const char __pyx_k_failed_to_set_the_component_indi[] = "failed to set the component indices";
 static const char __pyx_k_failed_to_upscale_subsampled_com[] = "failed to upscale subsampled components";
@@ -2752,10 +3010,11 @@ static const char __pyx_k_numpy_core_umath_failed_to_impor[] = "numpy.core.umath
 static const char __pyx_k_support_for_more_than_16_bits_pe[] = "support for more than 16-bits per component is not implemented";
 /* #### Code section: decls ### */
 static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
-static PyObject *__pyx_pf_9_openjpeg_6__defaults__(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
-static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_fp, PyObject *__pyx_v_codec, PyObject *__pyx_v_as_array); /* proto */
 static PyObject *__pyx_pf_9_openjpeg_8__defaults__(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
+static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_fp, PyObject *__pyx_v_codec, PyObject *__pyx_v_as_array); /* proto */
+static PyObject *__pyx_pf_9_openjpeg_10__defaults__(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
 static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_fp, PyObject *__pyx_v_codec); /* proto */
+static PyObject *__pyx_pf_9_openjpeg_6encode(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_arr, int __pyx_v_bits_stored, int __pyx_v_photometric_interpretation, int __pyx_v_use_mct, PyObject *__pyx_v_compression_ratios, PyObject *__pyx_v_signal_noise_ratios, int __pyx_v_codec_format); /* proto */
 /* #### Code section: late_includes ### */
 /* #### Code section: module_state ### */
 typedef struct {
@@ -2821,38 +3080,61 @@ typedef struct {
   PyTypeObject *__pyx_ptype_5numpy_ufunc;
   #if CYTHON_USE_MODULE_STATE
   #endif
+  PyObject *__pyx_kp_u_A_bits_stored_value_of;
   PyObject *__pyx_n_s_BinaryIO;
+  PyObject *__pyx_n_s_BytesIO;
   PyObject *__pyx_n_u_CYMK;
   PyObject *__pyx_n_s_Dict;
   PyObject *__pyx_kp_s_Dict_str_Union_str_int_bool;
   PyObject *__pyx_n_s_ERRORS;
   PyObject *__pyx_kp_u_Error_decoding_the_J2K_data;
-  PyObject *__pyx_kp_u_Error_decoding_the_J2K_data_2;
   PyObject *__pyx_n_s_ImportError;
+  PyObject *__pyx_kp_u_Invalid_value_for_the_bits_store;
   PyObject *__pyx_n_s_KeyError;
+  PyObject *__pyx_n_s_LOGGER;
+  PyObject *__pyx_n_s_List;
+  PyObject *__pyx_kp_u_More_than_10_compression_layers;
+  PyObject *__pyx_kp_u_Only_one_of_compression_ratios_o;
   PyObject *__pyx_n_s_RuntimeError;
+  PyObject *__pyx_kp_u_The_input_array_contains_values;
+  PyObject *__pyx_kp_u_The_input_array_has_an_unsupport;
+  PyObject *__pyx_kp_u_The_value_of_the_codec_format_pa;
+  PyObject *__pyx_n_s_Tuple;
+  PyObject *__pyx_kp_s_Tuple_int_bytes;
   PyObject *__pyx_n_s_Union;
   PyObject *__pyx_kp_s_Union_np_ndarray_bytearray;
+  PyObject *__pyx_n_s_ValueError;
   PyObject *__pyx_n_u_YUV;
-  PyObject *__pyx_n_s__12;
+  PyObject *__pyx_kp_u__10;
+  PyObject *__pyx_n_s__11;
+  PyObject *__pyx_n_s__20;
   PyObject *__pyx_kp_u__3;
   PyObject *__pyx_kp_u__4;
-  PyObject *__pyx_n_s__5;
+  PyObject *__pyx_kp_u__6;
+  PyObject *__pyx_n_s_allowed;
+  PyObject *__pyx_n_s_arr;
+  PyObject *__pyx_n_s_arr_max;
+  PyObject *__pyx_n_s_arr_min;
   PyObject *__pyx_n_s_as_array;
   PyObject *__pyx_n_s_asyncio_coroutines;
+  PyObject *__pyx_n_s_bits_stored;
   PyObject *__pyx_n_s_bool;
   PyObject *__pyx_n_s_bpp;
   PyObject *__pyx_n_s_bytes;
   PyObject *__pyx_n_s_ceil;
   PyObject *__pyx_n_s_cline_in_traceback;
   PyObject *__pyx_n_s_codec;
+  PyObject *__pyx_n_s_codec_format;
   PyObject *__pyx_n_s_colours;
   PyObject *__pyx_n_s_colourspace;
   PyObject *__pyx_n_u_colourspace;
   PyObject *__pyx_n_u_columns;
+  PyObject *__pyx_n_s_compression_ratios;
   PyObject *__pyx_n_s_decode;
+  PyObject *__pyx_n_s_dst;
   PyObject *__pyx_n_s_dtype;
   PyObject *__pyx_kp_u_e_YCC;
+  PyObject *__pyx_n_s_encode;
   PyObject *__pyx_kp_u_failed_to_create_the_input_strea;
   PyObject *__pyx_kp_u_failed_to_decode_image;
   PyObject *__pyx_kp_u_failed_to_read_the_header;
@@ -2861,16 +3143,26 @@ typedef struct {
   PyObject *__pyx_kp_u_failed_to_setup_the_decoder;
   PyObject *__pyx_kp_u_failed_to_upscale_subsampled_com;
   PyObject *__pyx_n_s_fp;
-  PyObject *__pyx_n_s_get;
+  PyObject *__pyx_n_s_getLogger;
   PyObject *__pyx_n_s_get_parameters;
   PyObject *__pyx_n_s_get_version;
+  PyObject *__pyx_n_s_getvalue;
   PyObject *__pyx_n_s_import;
   PyObject *__pyx_n_s_initializing;
   PyObject *__pyx_n_s_int;
+  PyObject *__pyx_n_s_int16;
+  PyObject *__pyx_n_s_int32;
+  PyObject *__pyx_n_s_int8;
+  PyObject *__pyx_n_s_io;
   PyObject *__pyx_n_s_is_coroutine;
+  PyObject *__pyx_kp_u_is_incompatible_with_the_range;
   PyObject *__pyx_n_u_is_signed;
+  PyObject *__pyx_n_s_itemsize;
+  PyObject *__pyx_n_s_logging;
   PyObject *__pyx_n_s_main;
   PyObject *__pyx_n_s_math;
+  PyObject *__pyx_n_s_max;
+  PyObject *__pyx_n_s_min;
   PyObject *__pyx_n_u_monochrome;
   PyObject *__pyx_n_s_msg;
   PyObject *__pyx_n_s_name;
@@ -2881,6 +3173,7 @@ typedef struct {
   PyObject *__pyx_n_s_numpy;
   PyObject *__pyx_kp_u_numpy_core_multiarray_failed_to;
   PyObject *__pyx_kp_u_numpy_core_umath_failed_to_impor;
+  PyObject *__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4;
   PyObject *__pyx_n_s_openjpeg;
   PyObject *__pyx_kp_s_openjpeg__openjpeg_pyx;
   PyObject *__pyx_n_s_out;
@@ -2889,20 +3182,27 @@ typedef struct {
   PyObject *__pyx_n_s_p_param;
   PyObject *__pyx_n_s_param;
   PyObject *__pyx_n_s_parameters;
+  PyObject *__pyx_n_s_photometric_interpretation;
   PyObject *__pyx_n_u_precision;
   PyObject *__pyx_n_s_ptr;
   PyObject *__pyx_n_s_result;
   PyObject *__pyx_n_s_return;
+  PyObject *__pyx_n_s_return_code;
   PyObject *__pyx_n_u_rows;
   PyObject *__pyx_n_u_sRGB;
+  PyObject *__pyx_n_s_signal_noise_ratios;
   PyObject *__pyx_n_s_spec;
   PyObject *__pyx_kp_u_support_for_more_than_16_bits_pe;
   PyObject *__pyx_n_s_test;
   PyObject *__pyx_n_s_typing;
+  PyObject *__pyx_n_s_uint16;
+  PyObject *__pyx_n_s_uint32;
   PyObject *__pyx_n_s_uint8;
   PyObject *__pyx_n_u_unknown;
   PyObject *__pyx_n_u_unspecified;
+  PyObject *__pyx_n_s_use_mct;
   PyObject *__pyx_n_s_version;
+  PyObject *__pyx_n_s_x;
   PyObject *__pyx_n_s_zeros;
   PyObject *__pyx_int_0;
   PyObject *__pyx_int_1;
@@ -2913,15 +3213,24 @@ typedef struct {
   PyObject *__pyx_int_6;
   PyObject *__pyx_int_7;
   PyObject *__pyx_int_8;
+  PyObject *__pyx_int_8388607;
+  PyObject *__pyx_int_16777215;
   PyObject *__pyx_int_neg_1;
+  PyObject *__pyx_int_neg_8388608;
   PyObject *__pyx_tuple_;
   PyObject *__pyx_tuple__2;
-  PyObject *__pyx_tuple__6;
+  PyObject *__pyx_tuple__5;
+  PyObject *__pyx_tuple__7;
   PyObject *__pyx_tuple__8;
-  PyObject *__pyx_tuple__10;
-  PyObject *__pyx_codeobj__7;
-  PyObject *__pyx_codeobj__9;
-  PyObject *__pyx_codeobj__11;
+  PyObject *__pyx_tuple__9;
+  PyObject *__pyx_tuple__12;
+  PyObject *__pyx_tuple__14;
+  PyObject *__pyx_tuple__16;
+  PyObject *__pyx_tuple__18;
+  PyObject *__pyx_codeobj__13;
+  PyObject *__pyx_codeobj__15;
+  PyObject *__pyx_codeobj__17;
+  PyObject *__pyx_codeobj__19;
 } __pyx_mstate;
 
 #if CYTHON_USE_MODULE_STATE
@@ -2980,38 +3289,61 @@ static int __pyx_m_clear(PyObject *m) {
   Py_CLEAR(clear_module_state->__pyx_ptype_5numpy_flexible);
   Py_CLEAR(clear_module_state->__pyx_ptype_5numpy_character);
   Py_CLEAR(clear_module_state->__pyx_ptype_5numpy_ufunc);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_A_bits_stored_value_of);
   Py_CLEAR(clear_module_state->__pyx_n_s_BinaryIO);
+  Py_CLEAR(clear_module_state->__pyx_n_s_BytesIO);
   Py_CLEAR(clear_module_state->__pyx_n_u_CYMK);
   Py_CLEAR(clear_module_state->__pyx_n_s_Dict);
   Py_CLEAR(clear_module_state->__pyx_kp_s_Dict_str_Union_str_int_bool);
   Py_CLEAR(clear_module_state->__pyx_n_s_ERRORS);
   Py_CLEAR(clear_module_state->__pyx_kp_u_Error_decoding_the_J2K_data);
-  Py_CLEAR(clear_module_state->__pyx_kp_u_Error_decoding_the_J2K_data_2);
   Py_CLEAR(clear_module_state->__pyx_n_s_ImportError);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_Invalid_value_for_the_bits_store);
   Py_CLEAR(clear_module_state->__pyx_n_s_KeyError);
+  Py_CLEAR(clear_module_state->__pyx_n_s_LOGGER);
+  Py_CLEAR(clear_module_state->__pyx_n_s_List);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_More_than_10_compression_layers);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_Only_one_of_compression_ratios_o);
   Py_CLEAR(clear_module_state->__pyx_n_s_RuntimeError);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_The_input_array_contains_values);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_The_input_array_has_an_unsupport);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_The_value_of_the_codec_format_pa);
+  Py_CLEAR(clear_module_state->__pyx_n_s_Tuple);
+  Py_CLEAR(clear_module_state->__pyx_kp_s_Tuple_int_bytes);
   Py_CLEAR(clear_module_state->__pyx_n_s_Union);
   Py_CLEAR(clear_module_state->__pyx_kp_s_Union_np_ndarray_bytearray);
+  Py_CLEAR(clear_module_state->__pyx_n_s_ValueError);
   Py_CLEAR(clear_module_state->__pyx_n_u_YUV);
-  Py_CLEAR(clear_module_state->__pyx_n_s__12);
+  Py_CLEAR(clear_module_state->__pyx_kp_u__10);
+  Py_CLEAR(clear_module_state->__pyx_n_s__11);
+  Py_CLEAR(clear_module_state->__pyx_n_s__20);
   Py_CLEAR(clear_module_state->__pyx_kp_u__3);
   Py_CLEAR(clear_module_state->__pyx_kp_u__4);
-  Py_CLEAR(clear_module_state->__pyx_n_s__5);
+  Py_CLEAR(clear_module_state->__pyx_kp_u__6);
+  Py_CLEAR(clear_module_state->__pyx_n_s_allowed);
+  Py_CLEAR(clear_module_state->__pyx_n_s_arr);
+  Py_CLEAR(clear_module_state->__pyx_n_s_arr_max);
+  Py_CLEAR(clear_module_state->__pyx_n_s_arr_min);
   Py_CLEAR(clear_module_state->__pyx_n_s_as_array);
   Py_CLEAR(clear_module_state->__pyx_n_s_asyncio_coroutines);
+  Py_CLEAR(clear_module_state->__pyx_n_s_bits_stored);
   Py_CLEAR(clear_module_state->__pyx_n_s_bool);
   Py_CLEAR(clear_module_state->__pyx_n_s_bpp);
   Py_CLEAR(clear_module_state->__pyx_n_s_bytes);
   Py_CLEAR(clear_module_state->__pyx_n_s_ceil);
   Py_CLEAR(clear_module_state->__pyx_n_s_cline_in_traceback);
   Py_CLEAR(clear_module_state->__pyx_n_s_codec);
+  Py_CLEAR(clear_module_state->__pyx_n_s_codec_format);
   Py_CLEAR(clear_module_state->__pyx_n_s_colours);
   Py_CLEAR(clear_module_state->__pyx_n_s_colourspace);
   Py_CLEAR(clear_module_state->__pyx_n_u_colourspace);
   Py_CLEAR(clear_module_state->__pyx_n_u_columns);
+  Py_CLEAR(clear_module_state->__pyx_n_s_compression_ratios);
   Py_CLEAR(clear_module_state->__pyx_n_s_decode);
+  Py_CLEAR(clear_module_state->__pyx_n_s_dst);
   Py_CLEAR(clear_module_state->__pyx_n_s_dtype);
   Py_CLEAR(clear_module_state->__pyx_kp_u_e_YCC);
+  Py_CLEAR(clear_module_state->__pyx_n_s_encode);
   Py_CLEAR(clear_module_state->__pyx_kp_u_failed_to_create_the_input_strea);
   Py_CLEAR(clear_module_state->__pyx_kp_u_failed_to_decode_image);
   Py_CLEAR(clear_module_state->__pyx_kp_u_failed_to_read_the_header);
@@ -3020,16 +3352,26 @@ static int __pyx_m_clear(PyObject *m) {
   Py_CLEAR(clear_module_state->__pyx_kp_u_failed_to_setup_the_decoder);
   Py_CLEAR(clear_module_state->__pyx_kp_u_failed_to_upscale_subsampled_com);
   Py_CLEAR(clear_module_state->__pyx_n_s_fp);
-  Py_CLEAR(clear_module_state->__pyx_n_s_get);
+  Py_CLEAR(clear_module_state->__pyx_n_s_getLogger);
   Py_CLEAR(clear_module_state->__pyx_n_s_get_parameters);
   Py_CLEAR(clear_module_state->__pyx_n_s_get_version);
+  Py_CLEAR(clear_module_state->__pyx_n_s_getvalue);
   Py_CLEAR(clear_module_state->__pyx_n_s_import);
   Py_CLEAR(clear_module_state->__pyx_n_s_initializing);
   Py_CLEAR(clear_module_state->__pyx_n_s_int);
+  Py_CLEAR(clear_module_state->__pyx_n_s_int16);
+  Py_CLEAR(clear_module_state->__pyx_n_s_int32);
+  Py_CLEAR(clear_module_state->__pyx_n_s_int8);
+  Py_CLEAR(clear_module_state->__pyx_n_s_io);
   Py_CLEAR(clear_module_state->__pyx_n_s_is_coroutine);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_is_incompatible_with_the_range);
   Py_CLEAR(clear_module_state->__pyx_n_u_is_signed);
+  Py_CLEAR(clear_module_state->__pyx_n_s_itemsize);
+  Py_CLEAR(clear_module_state->__pyx_n_s_logging);
   Py_CLEAR(clear_module_state->__pyx_n_s_main);
   Py_CLEAR(clear_module_state->__pyx_n_s_math);
+  Py_CLEAR(clear_module_state->__pyx_n_s_max);
+  Py_CLEAR(clear_module_state->__pyx_n_s_min);
   Py_CLEAR(clear_module_state->__pyx_n_u_monochrome);
   Py_CLEAR(clear_module_state->__pyx_n_s_msg);
   Py_CLEAR(clear_module_state->__pyx_n_s_name);
@@ -3040,6 +3382,7 @@ static int __pyx_m_clear(PyObject *m) {
   Py_CLEAR(clear_module_state->__pyx_n_s_numpy);
   Py_CLEAR(clear_module_state->__pyx_kp_u_numpy_core_multiarray_failed_to);
   Py_CLEAR(clear_module_state->__pyx_kp_u_numpy_core_umath_failed_to_impor);
+  Py_CLEAR(clear_module_state->__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4);
   Py_CLEAR(clear_module_state->__pyx_n_s_openjpeg);
   Py_CLEAR(clear_module_state->__pyx_kp_s_openjpeg__openjpeg_pyx);
   Py_CLEAR(clear_module_state->__pyx_n_s_out);
@@ -3048,20 +3391,27 @@ static int __pyx_m_clear(PyObject *m) {
   Py_CLEAR(clear_module_state->__pyx_n_s_p_param);
   Py_CLEAR(clear_module_state->__pyx_n_s_param);
   Py_CLEAR(clear_module_state->__pyx_n_s_parameters);
+  Py_CLEAR(clear_module_state->__pyx_n_s_photometric_interpretation);
   Py_CLEAR(clear_module_state->__pyx_n_u_precision);
   Py_CLEAR(clear_module_state->__pyx_n_s_ptr);
   Py_CLEAR(clear_module_state->__pyx_n_s_result);
   Py_CLEAR(clear_module_state->__pyx_n_s_return);
+  Py_CLEAR(clear_module_state->__pyx_n_s_return_code);
   Py_CLEAR(clear_module_state->__pyx_n_u_rows);
   Py_CLEAR(clear_module_state->__pyx_n_u_sRGB);
+  Py_CLEAR(clear_module_state->__pyx_n_s_signal_noise_ratios);
   Py_CLEAR(clear_module_state->__pyx_n_s_spec);
   Py_CLEAR(clear_module_state->__pyx_kp_u_support_for_more_than_16_bits_pe);
   Py_CLEAR(clear_module_state->__pyx_n_s_test);
   Py_CLEAR(clear_module_state->__pyx_n_s_typing);
+  Py_CLEAR(clear_module_state->__pyx_n_s_uint16);
+  Py_CLEAR(clear_module_state->__pyx_n_s_uint32);
   Py_CLEAR(clear_module_state->__pyx_n_s_uint8);
   Py_CLEAR(clear_module_state->__pyx_n_u_unknown);
   Py_CLEAR(clear_module_state->__pyx_n_u_unspecified);
+  Py_CLEAR(clear_module_state->__pyx_n_s_use_mct);
   Py_CLEAR(clear_module_state->__pyx_n_s_version);
+  Py_CLEAR(clear_module_state->__pyx_n_s_x);
   Py_CLEAR(clear_module_state->__pyx_n_s_zeros);
   Py_CLEAR(clear_module_state->__pyx_int_0);
   Py_CLEAR(clear_module_state->__pyx_int_1);
@@ -3072,15 +3422,24 @@ static int __pyx_m_clear(PyObject *m) {
   Py_CLEAR(clear_module_state->__pyx_int_6);
   Py_CLEAR(clear_module_state->__pyx_int_7);
   Py_CLEAR(clear_module_state->__pyx_int_8);
+  Py_CLEAR(clear_module_state->__pyx_int_8388607);
+  Py_CLEAR(clear_module_state->__pyx_int_16777215);
   Py_CLEAR(clear_module_state->__pyx_int_neg_1);
+  Py_CLEAR(clear_module_state->__pyx_int_neg_8388608);
   Py_CLEAR(clear_module_state->__pyx_tuple_);
   Py_CLEAR(clear_module_state->__pyx_tuple__2);
-  Py_CLEAR(clear_module_state->__pyx_tuple__6);
+  Py_CLEAR(clear_module_state->__pyx_tuple__5);
+  Py_CLEAR(clear_module_state->__pyx_tuple__7);
   Py_CLEAR(clear_module_state->__pyx_tuple__8);
-  Py_CLEAR(clear_module_state->__pyx_tuple__10);
-  Py_CLEAR(clear_module_state->__pyx_codeobj__7);
-  Py_CLEAR(clear_module_state->__pyx_codeobj__9);
-  Py_CLEAR(clear_module_state->__pyx_codeobj__11);
+  Py_CLEAR(clear_module_state->__pyx_tuple__9);
+  Py_CLEAR(clear_module_state->__pyx_tuple__12);
+  Py_CLEAR(clear_module_state->__pyx_tuple__14);
+  Py_CLEAR(clear_module_state->__pyx_tuple__16);
+  Py_CLEAR(clear_module_state->__pyx_tuple__18);
+  Py_CLEAR(clear_module_state->__pyx_codeobj__13);
+  Py_CLEAR(clear_module_state->__pyx_codeobj__15);
+  Py_CLEAR(clear_module_state->__pyx_codeobj__17);
+  Py_CLEAR(clear_module_state->__pyx_codeobj__19);
   return 0;
 }
 #endif
@@ -3117,38 +3476,61 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
   Py_VISIT(traverse_module_state->__pyx_ptype_5numpy_flexible);
   Py_VISIT(traverse_module_state->__pyx_ptype_5numpy_character);
   Py_VISIT(traverse_module_state->__pyx_ptype_5numpy_ufunc);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_A_bits_stored_value_of);
   Py_VISIT(traverse_module_state->__pyx_n_s_BinaryIO);
+  Py_VISIT(traverse_module_state->__pyx_n_s_BytesIO);
   Py_VISIT(traverse_module_state->__pyx_n_u_CYMK);
   Py_VISIT(traverse_module_state->__pyx_n_s_Dict);
   Py_VISIT(traverse_module_state->__pyx_kp_s_Dict_str_Union_str_int_bool);
   Py_VISIT(traverse_module_state->__pyx_n_s_ERRORS);
   Py_VISIT(traverse_module_state->__pyx_kp_u_Error_decoding_the_J2K_data);
-  Py_VISIT(traverse_module_state->__pyx_kp_u_Error_decoding_the_J2K_data_2);
   Py_VISIT(traverse_module_state->__pyx_n_s_ImportError);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_Invalid_value_for_the_bits_store);
   Py_VISIT(traverse_module_state->__pyx_n_s_KeyError);
+  Py_VISIT(traverse_module_state->__pyx_n_s_LOGGER);
+  Py_VISIT(traverse_module_state->__pyx_n_s_List);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_More_than_10_compression_layers);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_Only_one_of_compression_ratios_o);
   Py_VISIT(traverse_module_state->__pyx_n_s_RuntimeError);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_The_input_array_contains_values);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_The_input_array_has_an_unsupport);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_The_value_of_the_codec_format_pa);
+  Py_VISIT(traverse_module_state->__pyx_n_s_Tuple);
+  Py_VISIT(traverse_module_state->__pyx_kp_s_Tuple_int_bytes);
   Py_VISIT(traverse_module_state->__pyx_n_s_Union);
   Py_VISIT(traverse_module_state->__pyx_kp_s_Union_np_ndarray_bytearray);
+  Py_VISIT(traverse_module_state->__pyx_n_s_ValueError);
   Py_VISIT(traverse_module_state->__pyx_n_u_YUV);
-  Py_VISIT(traverse_module_state->__pyx_n_s__12);
+  Py_VISIT(traverse_module_state->__pyx_kp_u__10);
+  Py_VISIT(traverse_module_state->__pyx_n_s__11);
+  Py_VISIT(traverse_module_state->__pyx_n_s__20);
   Py_VISIT(traverse_module_state->__pyx_kp_u__3);
   Py_VISIT(traverse_module_state->__pyx_kp_u__4);
-  Py_VISIT(traverse_module_state->__pyx_n_s__5);
+  Py_VISIT(traverse_module_state->__pyx_kp_u__6);
+  Py_VISIT(traverse_module_state->__pyx_n_s_allowed);
+  Py_VISIT(traverse_module_state->__pyx_n_s_arr);
+  Py_VISIT(traverse_module_state->__pyx_n_s_arr_max);
+  Py_VISIT(traverse_module_state->__pyx_n_s_arr_min);
   Py_VISIT(traverse_module_state->__pyx_n_s_as_array);
   Py_VISIT(traverse_module_state->__pyx_n_s_asyncio_coroutines);
+  Py_VISIT(traverse_module_state->__pyx_n_s_bits_stored);
   Py_VISIT(traverse_module_state->__pyx_n_s_bool);
   Py_VISIT(traverse_module_state->__pyx_n_s_bpp);
   Py_VISIT(traverse_module_state->__pyx_n_s_bytes);
   Py_VISIT(traverse_module_state->__pyx_n_s_ceil);
   Py_VISIT(traverse_module_state->__pyx_n_s_cline_in_traceback);
   Py_VISIT(traverse_module_state->__pyx_n_s_codec);
+  Py_VISIT(traverse_module_state->__pyx_n_s_codec_format);
   Py_VISIT(traverse_module_state->__pyx_n_s_colours);
   Py_VISIT(traverse_module_state->__pyx_n_s_colourspace);
   Py_VISIT(traverse_module_state->__pyx_n_u_colourspace);
   Py_VISIT(traverse_module_state->__pyx_n_u_columns);
+  Py_VISIT(traverse_module_state->__pyx_n_s_compression_ratios);
   Py_VISIT(traverse_module_state->__pyx_n_s_decode);
+  Py_VISIT(traverse_module_state->__pyx_n_s_dst);
   Py_VISIT(traverse_module_state->__pyx_n_s_dtype);
   Py_VISIT(traverse_module_state->__pyx_kp_u_e_YCC);
+  Py_VISIT(traverse_module_state->__pyx_n_s_encode);
   Py_VISIT(traverse_module_state->__pyx_kp_u_failed_to_create_the_input_strea);
   Py_VISIT(traverse_module_state->__pyx_kp_u_failed_to_decode_image);
   Py_VISIT(traverse_module_state->__pyx_kp_u_failed_to_read_the_header);
@@ -3157,16 +3539,26 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
   Py_VISIT(traverse_module_state->__pyx_kp_u_failed_to_setup_the_decoder);
   Py_VISIT(traverse_module_state->__pyx_kp_u_failed_to_upscale_subsampled_com);
   Py_VISIT(traverse_module_state->__pyx_n_s_fp);
-  Py_VISIT(traverse_module_state->__pyx_n_s_get);
+  Py_VISIT(traverse_module_state->__pyx_n_s_getLogger);
   Py_VISIT(traverse_module_state->__pyx_n_s_get_parameters);
   Py_VISIT(traverse_module_state->__pyx_n_s_get_version);
+  Py_VISIT(traverse_module_state->__pyx_n_s_getvalue);
   Py_VISIT(traverse_module_state->__pyx_n_s_import);
   Py_VISIT(traverse_module_state->__pyx_n_s_initializing);
   Py_VISIT(traverse_module_state->__pyx_n_s_int);
+  Py_VISIT(traverse_module_state->__pyx_n_s_int16);
+  Py_VISIT(traverse_module_state->__pyx_n_s_int32);
+  Py_VISIT(traverse_module_state->__pyx_n_s_int8);
+  Py_VISIT(traverse_module_state->__pyx_n_s_io);
   Py_VISIT(traverse_module_state->__pyx_n_s_is_coroutine);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_is_incompatible_with_the_range);
   Py_VISIT(traverse_module_state->__pyx_n_u_is_signed);
+  Py_VISIT(traverse_module_state->__pyx_n_s_itemsize);
+  Py_VISIT(traverse_module_state->__pyx_n_s_logging);
   Py_VISIT(traverse_module_state->__pyx_n_s_main);
   Py_VISIT(traverse_module_state->__pyx_n_s_math);
+  Py_VISIT(traverse_module_state->__pyx_n_s_max);
+  Py_VISIT(traverse_module_state->__pyx_n_s_min);
   Py_VISIT(traverse_module_state->__pyx_n_u_monochrome);
   Py_VISIT(traverse_module_state->__pyx_n_s_msg);
   Py_VISIT(traverse_module_state->__pyx_n_s_name);
@@ -3177,6 +3569,7 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
   Py_VISIT(traverse_module_state->__pyx_n_s_numpy);
   Py_VISIT(traverse_module_state->__pyx_kp_u_numpy_core_multiarray_failed_to);
   Py_VISIT(traverse_module_state->__pyx_kp_u_numpy_core_umath_failed_to_impor);
+  Py_VISIT(traverse_module_state->__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4);
   Py_VISIT(traverse_module_state->__pyx_n_s_openjpeg);
   Py_VISIT(traverse_module_state->__pyx_kp_s_openjpeg__openjpeg_pyx);
   Py_VISIT(traverse_module_state->__pyx_n_s_out);
@@ -3185,20 +3578,27 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
   Py_VISIT(traverse_module_state->__pyx_n_s_p_param);
   Py_VISIT(traverse_module_state->__pyx_n_s_param);
   Py_VISIT(traverse_module_state->__pyx_n_s_parameters);
+  Py_VISIT(traverse_module_state->__pyx_n_s_photometric_interpretation);
   Py_VISIT(traverse_module_state->__pyx_n_u_precision);
   Py_VISIT(traverse_module_state->__pyx_n_s_ptr);
   Py_VISIT(traverse_module_state->__pyx_n_s_result);
   Py_VISIT(traverse_module_state->__pyx_n_s_return);
+  Py_VISIT(traverse_module_state->__pyx_n_s_return_code);
   Py_VISIT(traverse_module_state->__pyx_n_u_rows);
   Py_VISIT(traverse_module_state->__pyx_n_u_sRGB);
+  Py_VISIT(traverse_module_state->__pyx_n_s_signal_noise_ratios);
   Py_VISIT(traverse_module_state->__pyx_n_s_spec);
   Py_VISIT(traverse_module_state->__pyx_kp_u_support_for_more_than_16_bits_pe);
   Py_VISIT(traverse_module_state->__pyx_n_s_test);
   Py_VISIT(traverse_module_state->__pyx_n_s_typing);
+  Py_VISIT(traverse_module_state->__pyx_n_s_uint16);
+  Py_VISIT(traverse_module_state->__pyx_n_s_uint32);
   Py_VISIT(traverse_module_state->__pyx_n_s_uint8);
   Py_VISIT(traverse_module_state->__pyx_n_u_unknown);
   Py_VISIT(traverse_module_state->__pyx_n_u_unspecified);
+  Py_VISIT(traverse_module_state->__pyx_n_s_use_mct);
   Py_VISIT(traverse_module_state->__pyx_n_s_version);
+  Py_VISIT(traverse_module_state->__pyx_n_s_x);
   Py_VISIT(traverse_module_state->__pyx_n_s_zeros);
   Py_VISIT(traverse_module_state->__pyx_int_0);
   Py_VISIT(traverse_module_state->__pyx_int_1);
@@ -3209,15 +3609,24 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
   Py_VISIT(traverse_module_state->__pyx_int_6);
   Py_VISIT(traverse_module_state->__pyx_int_7);
   Py_VISIT(traverse_module_state->__pyx_int_8);
+  Py_VISIT(traverse_module_state->__pyx_int_8388607);
+  Py_VISIT(traverse_module_state->__pyx_int_16777215);
   Py_VISIT(traverse_module_state->__pyx_int_neg_1);
+  Py_VISIT(traverse_module_state->__pyx_int_neg_8388608);
   Py_VISIT(traverse_module_state->__pyx_tuple_);
   Py_VISIT(traverse_module_state->__pyx_tuple__2);
-  Py_VISIT(traverse_module_state->__pyx_tuple__6);
+  Py_VISIT(traverse_module_state->__pyx_tuple__5);
+  Py_VISIT(traverse_module_state->__pyx_tuple__7);
   Py_VISIT(traverse_module_state->__pyx_tuple__8);
-  Py_VISIT(traverse_module_state->__pyx_tuple__10);
-  Py_VISIT(traverse_module_state->__pyx_codeobj__7);
-  Py_VISIT(traverse_module_state->__pyx_codeobj__9);
-  Py_VISIT(traverse_module_state->__pyx_codeobj__11);
+  Py_VISIT(traverse_module_state->__pyx_tuple__9);
+  Py_VISIT(traverse_module_state->__pyx_tuple__12);
+  Py_VISIT(traverse_module_state->__pyx_tuple__14);
+  Py_VISIT(traverse_module_state->__pyx_tuple__16);
+  Py_VISIT(traverse_module_state->__pyx_tuple__18);
+  Py_VISIT(traverse_module_state->__pyx_codeobj__13);
+  Py_VISIT(traverse_module_state->__pyx_codeobj__15);
+  Py_VISIT(traverse_module_state->__pyx_codeobj__17);
+  Py_VISIT(traverse_module_state->__pyx_codeobj__19);
   return 0;
 }
 #endif
@@ -3284,38 +3693,61 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 #define __pyx_ptype_5numpy_ufunc __pyx_mstate_global->__pyx_ptype_5numpy_ufunc
 #if CYTHON_USE_MODULE_STATE
 #endif
+#define __pyx_kp_u_A_bits_stored_value_of __pyx_mstate_global->__pyx_kp_u_A_bits_stored_value_of
 #define __pyx_n_s_BinaryIO __pyx_mstate_global->__pyx_n_s_BinaryIO
+#define __pyx_n_s_BytesIO __pyx_mstate_global->__pyx_n_s_BytesIO
 #define __pyx_n_u_CYMK __pyx_mstate_global->__pyx_n_u_CYMK
 #define __pyx_n_s_Dict __pyx_mstate_global->__pyx_n_s_Dict
 #define __pyx_kp_s_Dict_str_Union_str_int_bool __pyx_mstate_global->__pyx_kp_s_Dict_str_Union_str_int_bool
 #define __pyx_n_s_ERRORS __pyx_mstate_global->__pyx_n_s_ERRORS
 #define __pyx_kp_u_Error_decoding_the_J2K_data __pyx_mstate_global->__pyx_kp_u_Error_decoding_the_J2K_data
-#define __pyx_kp_u_Error_decoding_the_J2K_data_2 __pyx_mstate_global->__pyx_kp_u_Error_decoding_the_J2K_data_2
 #define __pyx_n_s_ImportError __pyx_mstate_global->__pyx_n_s_ImportError
+#define __pyx_kp_u_Invalid_value_for_the_bits_store __pyx_mstate_global->__pyx_kp_u_Invalid_value_for_the_bits_store
 #define __pyx_n_s_KeyError __pyx_mstate_global->__pyx_n_s_KeyError
+#define __pyx_n_s_LOGGER __pyx_mstate_global->__pyx_n_s_LOGGER
+#define __pyx_n_s_List __pyx_mstate_global->__pyx_n_s_List
+#define __pyx_kp_u_More_than_10_compression_layers __pyx_mstate_global->__pyx_kp_u_More_than_10_compression_layers
+#define __pyx_kp_u_Only_one_of_compression_ratios_o __pyx_mstate_global->__pyx_kp_u_Only_one_of_compression_ratios_o
 #define __pyx_n_s_RuntimeError __pyx_mstate_global->__pyx_n_s_RuntimeError
+#define __pyx_kp_u_The_input_array_contains_values __pyx_mstate_global->__pyx_kp_u_The_input_array_contains_values
+#define __pyx_kp_u_The_input_array_has_an_unsupport __pyx_mstate_global->__pyx_kp_u_The_input_array_has_an_unsupport
+#define __pyx_kp_u_The_value_of_the_codec_format_pa __pyx_mstate_global->__pyx_kp_u_The_value_of_the_codec_format_pa
+#define __pyx_n_s_Tuple __pyx_mstate_global->__pyx_n_s_Tuple
+#define __pyx_kp_s_Tuple_int_bytes __pyx_mstate_global->__pyx_kp_s_Tuple_int_bytes
 #define __pyx_n_s_Union __pyx_mstate_global->__pyx_n_s_Union
 #define __pyx_kp_s_Union_np_ndarray_bytearray __pyx_mstate_global->__pyx_kp_s_Union_np_ndarray_bytearray
+#define __pyx_n_s_ValueError __pyx_mstate_global->__pyx_n_s_ValueError
 #define __pyx_n_u_YUV __pyx_mstate_global->__pyx_n_u_YUV
-#define __pyx_n_s__12 __pyx_mstate_global->__pyx_n_s__12
+#define __pyx_kp_u__10 __pyx_mstate_global->__pyx_kp_u__10
+#define __pyx_n_s__11 __pyx_mstate_global->__pyx_n_s__11
+#define __pyx_n_s__20 __pyx_mstate_global->__pyx_n_s__20
 #define __pyx_kp_u__3 __pyx_mstate_global->__pyx_kp_u__3
 #define __pyx_kp_u__4 __pyx_mstate_global->__pyx_kp_u__4
-#define __pyx_n_s__5 __pyx_mstate_global->__pyx_n_s__5
+#define __pyx_kp_u__6 __pyx_mstate_global->__pyx_kp_u__6
+#define __pyx_n_s_allowed __pyx_mstate_global->__pyx_n_s_allowed
+#define __pyx_n_s_arr __pyx_mstate_global->__pyx_n_s_arr
+#define __pyx_n_s_arr_max __pyx_mstate_global->__pyx_n_s_arr_max
+#define __pyx_n_s_arr_min __pyx_mstate_global->__pyx_n_s_arr_min
 #define __pyx_n_s_as_array __pyx_mstate_global->__pyx_n_s_as_array
 #define __pyx_n_s_asyncio_coroutines __pyx_mstate_global->__pyx_n_s_asyncio_coroutines
+#define __pyx_n_s_bits_stored __pyx_mstate_global->__pyx_n_s_bits_stored
 #define __pyx_n_s_bool __pyx_mstate_global->__pyx_n_s_bool
 #define __pyx_n_s_bpp __pyx_mstate_global->__pyx_n_s_bpp
 #define __pyx_n_s_bytes __pyx_mstate_global->__pyx_n_s_bytes
 #define __pyx_n_s_ceil __pyx_mstate_global->__pyx_n_s_ceil
 #define __pyx_n_s_cline_in_traceback __pyx_mstate_global->__pyx_n_s_cline_in_traceback
 #define __pyx_n_s_codec __pyx_mstate_global->__pyx_n_s_codec
+#define __pyx_n_s_codec_format __pyx_mstate_global->__pyx_n_s_codec_format
 #define __pyx_n_s_colours __pyx_mstate_global->__pyx_n_s_colours
 #define __pyx_n_s_colourspace __pyx_mstate_global->__pyx_n_s_colourspace
 #define __pyx_n_u_colourspace __pyx_mstate_global->__pyx_n_u_colourspace
 #define __pyx_n_u_columns __pyx_mstate_global->__pyx_n_u_columns
+#define __pyx_n_s_compression_ratios __pyx_mstate_global->__pyx_n_s_compression_ratios
 #define __pyx_n_s_decode __pyx_mstate_global->__pyx_n_s_decode
+#define __pyx_n_s_dst __pyx_mstate_global->__pyx_n_s_dst
 #define __pyx_n_s_dtype __pyx_mstate_global->__pyx_n_s_dtype
 #define __pyx_kp_u_e_YCC __pyx_mstate_global->__pyx_kp_u_e_YCC
+#define __pyx_n_s_encode __pyx_mstate_global->__pyx_n_s_encode
 #define __pyx_kp_u_failed_to_create_the_input_strea __pyx_mstate_global->__pyx_kp_u_failed_to_create_the_input_strea
 #define __pyx_kp_u_failed_to_decode_image __pyx_mstate_global->__pyx_kp_u_failed_to_decode_image
 #define __pyx_kp_u_failed_to_read_the_header __pyx_mstate_global->__pyx_kp_u_failed_to_read_the_header
@@ -3324,16 +3756,26 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 #define __pyx_kp_u_failed_to_setup_the_decoder __pyx_mstate_global->__pyx_kp_u_failed_to_setup_the_decoder
 #define __pyx_kp_u_failed_to_upscale_subsampled_com __pyx_mstate_global->__pyx_kp_u_failed_to_upscale_subsampled_com
 #define __pyx_n_s_fp __pyx_mstate_global->__pyx_n_s_fp
-#define __pyx_n_s_get __pyx_mstate_global->__pyx_n_s_get
+#define __pyx_n_s_getLogger __pyx_mstate_global->__pyx_n_s_getLogger
 #define __pyx_n_s_get_parameters __pyx_mstate_global->__pyx_n_s_get_parameters
 #define __pyx_n_s_get_version __pyx_mstate_global->__pyx_n_s_get_version
+#define __pyx_n_s_getvalue __pyx_mstate_global->__pyx_n_s_getvalue
 #define __pyx_n_s_import __pyx_mstate_global->__pyx_n_s_import
 #define __pyx_n_s_initializing __pyx_mstate_global->__pyx_n_s_initializing
 #define __pyx_n_s_int __pyx_mstate_global->__pyx_n_s_int
+#define __pyx_n_s_int16 __pyx_mstate_global->__pyx_n_s_int16
+#define __pyx_n_s_int32 __pyx_mstate_global->__pyx_n_s_int32
+#define __pyx_n_s_int8 __pyx_mstate_global->__pyx_n_s_int8
+#define __pyx_n_s_io __pyx_mstate_global->__pyx_n_s_io
 #define __pyx_n_s_is_coroutine __pyx_mstate_global->__pyx_n_s_is_coroutine
+#define __pyx_kp_u_is_incompatible_with_the_range __pyx_mstate_global->__pyx_kp_u_is_incompatible_with_the_range
 #define __pyx_n_u_is_signed __pyx_mstate_global->__pyx_n_u_is_signed
+#define __pyx_n_s_itemsize __pyx_mstate_global->__pyx_n_s_itemsize
+#define __pyx_n_s_logging __pyx_mstate_global->__pyx_n_s_logging
 #define __pyx_n_s_main __pyx_mstate_global->__pyx_n_s_main
 #define __pyx_n_s_math __pyx_mstate_global->__pyx_n_s_math
+#define __pyx_n_s_max __pyx_mstate_global->__pyx_n_s_max
+#define __pyx_n_s_min __pyx_mstate_global->__pyx_n_s_min
 #define __pyx_n_u_monochrome __pyx_mstate_global->__pyx_n_u_monochrome
 #define __pyx_n_s_msg __pyx_mstate_global->__pyx_n_s_msg
 #define __pyx_n_s_name __pyx_mstate_global->__pyx_n_s_name
@@ -3344,6 +3786,7 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 #define __pyx_n_s_numpy __pyx_mstate_global->__pyx_n_s_numpy
 #define __pyx_kp_u_numpy_core_multiarray_failed_to __pyx_mstate_global->__pyx_kp_u_numpy_core_multiarray_failed_to
 #define __pyx_kp_u_numpy_core_umath_failed_to_impor __pyx_mstate_global->__pyx_kp_u_numpy_core_umath_failed_to_impor
+#define __pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4 __pyx_mstate_global->__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4
 #define __pyx_n_s_openjpeg __pyx_mstate_global->__pyx_n_s_openjpeg
 #define __pyx_kp_s_openjpeg__openjpeg_pyx __pyx_mstate_global->__pyx_kp_s_openjpeg__openjpeg_pyx
 #define __pyx_n_s_out __pyx_mstate_global->__pyx_n_s_out
@@ -3352,20 +3795,27 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 #define __pyx_n_s_p_param __pyx_mstate_global->__pyx_n_s_p_param
 #define __pyx_n_s_param __pyx_mstate_global->__pyx_n_s_param
 #define __pyx_n_s_parameters __pyx_mstate_global->__pyx_n_s_parameters
+#define __pyx_n_s_photometric_interpretation __pyx_mstate_global->__pyx_n_s_photometric_interpretation
 #define __pyx_n_u_precision __pyx_mstate_global->__pyx_n_u_precision
 #define __pyx_n_s_ptr __pyx_mstate_global->__pyx_n_s_ptr
 #define __pyx_n_s_result __pyx_mstate_global->__pyx_n_s_result
 #define __pyx_n_s_return __pyx_mstate_global->__pyx_n_s_return
+#define __pyx_n_s_return_code __pyx_mstate_global->__pyx_n_s_return_code
 #define __pyx_n_u_rows __pyx_mstate_global->__pyx_n_u_rows
 #define __pyx_n_u_sRGB __pyx_mstate_global->__pyx_n_u_sRGB
+#define __pyx_n_s_signal_noise_ratios __pyx_mstate_global->__pyx_n_s_signal_noise_ratios
 #define __pyx_n_s_spec __pyx_mstate_global->__pyx_n_s_spec
 #define __pyx_kp_u_support_for_more_than_16_bits_pe __pyx_mstate_global->__pyx_kp_u_support_for_more_than_16_bits_pe
 #define __pyx_n_s_test __pyx_mstate_global->__pyx_n_s_test
 #define __pyx_n_s_typing __pyx_mstate_global->__pyx_n_s_typing
+#define __pyx_n_s_uint16 __pyx_mstate_global->__pyx_n_s_uint16
+#define __pyx_n_s_uint32 __pyx_mstate_global->__pyx_n_s_uint32
 #define __pyx_n_s_uint8 __pyx_mstate_global->__pyx_n_s_uint8
 #define __pyx_n_u_unknown __pyx_mstate_global->__pyx_n_u_unknown
 #define __pyx_n_u_unspecified __pyx_mstate_global->__pyx_n_u_unspecified
+#define __pyx_n_s_use_mct __pyx_mstate_global->__pyx_n_s_use_mct
 #define __pyx_n_s_version __pyx_mstate_global->__pyx_n_s_version
+#define __pyx_n_s_x __pyx_mstate_global->__pyx_n_s_x
 #define __pyx_n_s_zeros __pyx_mstate_global->__pyx_n_s_zeros
 #define __pyx_int_0 __pyx_mstate_global->__pyx_int_0
 #define __pyx_int_1 __pyx_mstate_global->__pyx_int_1
@@ -3376,18 +3826,27 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 #define __pyx_int_6 __pyx_mstate_global->__pyx_int_6
 #define __pyx_int_7 __pyx_mstate_global->__pyx_int_7
 #define __pyx_int_8 __pyx_mstate_global->__pyx_int_8
+#define __pyx_int_8388607 __pyx_mstate_global->__pyx_int_8388607
+#define __pyx_int_16777215 __pyx_mstate_global->__pyx_int_16777215
 #define __pyx_int_neg_1 __pyx_mstate_global->__pyx_int_neg_1
+#define __pyx_int_neg_8388608 __pyx_mstate_global->__pyx_int_neg_8388608
 #define __pyx_tuple_ __pyx_mstate_global->__pyx_tuple_
 #define __pyx_tuple__2 __pyx_mstate_global->__pyx_tuple__2
-#define __pyx_tuple__6 __pyx_mstate_global->__pyx_tuple__6
+#define __pyx_tuple__5 __pyx_mstate_global->__pyx_tuple__5
+#define __pyx_tuple__7 __pyx_mstate_global->__pyx_tuple__7
 #define __pyx_tuple__8 __pyx_mstate_global->__pyx_tuple__8
-#define __pyx_tuple__10 __pyx_mstate_global->__pyx_tuple__10
-#define __pyx_codeobj__7 __pyx_mstate_global->__pyx_codeobj__7
-#define __pyx_codeobj__9 __pyx_mstate_global->__pyx_codeobj__9
-#define __pyx_codeobj__11 __pyx_mstate_global->__pyx_codeobj__11
+#define __pyx_tuple__9 __pyx_mstate_global->__pyx_tuple__9
+#define __pyx_tuple__12 __pyx_mstate_global->__pyx_tuple__12
+#define __pyx_tuple__14 __pyx_mstate_global->__pyx_tuple__14
+#define __pyx_tuple__16 __pyx_mstate_global->__pyx_tuple__16
+#define __pyx_tuple__18 __pyx_mstate_global->__pyx_tuple__18
+#define __pyx_codeobj__13 __pyx_mstate_global->__pyx_codeobj__13
+#define __pyx_codeobj__15 __pyx_mstate_global->__pyx_codeobj__15
+#define __pyx_codeobj__17 __pyx_mstate_global->__pyx_codeobj__17
+#define __pyx_codeobj__19 __pyx_mstate_global->__pyx_codeobj__19
 /* #### Code section: module_code ### */
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":245
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":245
  * 
  *         @property
  *         cdef inline PyObject* base(self) nogil:             # <<<<<<<<<<<<<<
@@ -3398,7 +3857,7 @@ static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
 static CYTHON_INLINE PyObject *__pyx_f_5numpy_7ndarray_4base_base(PyArrayObject *__pyx_v_self) {
   PyObject *__pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":248
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":248
  *             """Returns a borrowed reference to the object owning the data/memory.
  *             """
  *             return PyArray_BASE(self)             # <<<<<<<<<<<<<<
@@ -3408,7 +3867,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_7ndarray_4base_base(PyArrayObject
   __pyx_r = PyArray_BASE(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":245
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":245
  * 
  *         @property
  *         cdef inline PyObject* base(self) nogil:             # <<<<<<<<<<<<<<
@@ -3421,7 +3880,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_7ndarray_4base_base(PyArrayObject
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":251
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":251
  * 
  *         @property
  *         cdef inline dtype descr(self):             # <<<<<<<<<<<<<<
@@ -3435,7 +3894,7 @@ static CYTHON_INLINE PyArray_Descr *__pyx_f_5numpy_7ndarray_5descr_descr(PyArray
   PyArray_Descr *__pyx_t_1;
   __Pyx_RefNannySetupContext("descr", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":254
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":254
  *             """Returns an owned reference to the dtype of the array.
  *             """
  *             return <dtype>PyArray_DESCR(self)             # <<<<<<<<<<<<<<
@@ -3448,7 +3907,7 @@ static CYTHON_INLINE PyArray_Descr *__pyx_f_5numpy_7ndarray_5descr_descr(PyArray
   __pyx_r = ((PyArray_Descr *)__pyx_t_1);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":251
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":251
  * 
  *         @property
  *         cdef inline dtype descr(self):             # <<<<<<<<<<<<<<
@@ -3463,7 +3922,7 @@ static CYTHON_INLINE PyArray_Descr *__pyx_f_5numpy_7ndarray_5descr_descr(PyArray
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":257
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":257
  * 
  *         @property
  *         cdef inline int ndim(self) nogil:             # <<<<<<<<<<<<<<
@@ -3474,7 +3933,7 @@ static CYTHON_INLINE PyArray_Descr *__pyx_f_5numpy_7ndarray_5descr_descr(PyArray
 static CYTHON_INLINE int __pyx_f_5numpy_7ndarray_4ndim_ndim(PyArrayObject *__pyx_v_self) {
   int __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":260
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":260
  *             """Returns the number of dimensions in the array.
  *             """
  *             return PyArray_NDIM(self)             # <<<<<<<<<<<<<<
@@ -3484,7 +3943,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_7ndarray_4ndim_ndim(PyArrayObject *__pyx
   __pyx_r = PyArray_NDIM(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":257
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":257
  * 
  *         @property
  *         cdef inline int ndim(self) nogil:             # <<<<<<<<<<<<<<
@@ -3497,7 +3956,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_7ndarray_4ndim_ndim(PyArrayObject *__pyx
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":263
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":263
  * 
  *         @property
  *         cdef inline npy_intp *shape(self) nogil:             # <<<<<<<<<<<<<<
@@ -3508,7 +3967,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_7ndarray_4ndim_ndim(PyArrayObject *__pyx
 static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_5shape_shape(PyArrayObject *__pyx_v_self) {
   npy_intp *__pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":268
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":268
  *             Can return NULL for 0-dimensional arrays.
  *             """
  *             return PyArray_DIMS(self)             # <<<<<<<<<<<<<<
@@ -3518,7 +3977,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_5shape_shape(PyArrayObjec
   __pyx_r = PyArray_DIMS(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":263
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":263
  * 
  *         @property
  *         cdef inline npy_intp *shape(self) nogil:             # <<<<<<<<<<<<<<
@@ -3531,7 +3990,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_5shape_shape(PyArrayObjec
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":271
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":271
  * 
  *         @property
  *         cdef inline npy_intp *strides(self) nogil:             # <<<<<<<<<<<<<<
@@ -3542,7 +4001,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_5shape_shape(PyArrayObjec
 static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_7strides_strides(PyArrayObject *__pyx_v_self) {
   npy_intp *__pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":275
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":275
  *             The number of elements matches the number of dimensions of the array (ndim).
  *             """
  *             return PyArray_STRIDES(self)             # <<<<<<<<<<<<<<
@@ -3552,7 +4011,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_7strides_strides(PyArrayO
   __pyx_r = PyArray_STRIDES(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":271
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":271
  * 
  *         @property
  *         cdef inline npy_intp *strides(self) nogil:             # <<<<<<<<<<<<<<
@@ -3565,7 +4024,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_7strides_strides(PyArrayO
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":278
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":278
  * 
  *         @property
  *         cdef inline npy_intp size(self) nogil:             # <<<<<<<<<<<<<<
@@ -3576,7 +4035,7 @@ static CYTHON_INLINE npy_intp *__pyx_f_5numpy_7ndarray_7strides_strides(PyArrayO
 static CYTHON_INLINE npy_intp __pyx_f_5numpy_7ndarray_4size_size(PyArrayObject *__pyx_v_self) {
   npy_intp __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":281
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":281
  *             """Returns the total size (in number of elements) of the array.
  *             """
  *             return PyArray_SIZE(self)             # <<<<<<<<<<<<<<
@@ -3586,7 +4045,7 @@ static CYTHON_INLINE npy_intp __pyx_f_5numpy_7ndarray_4size_size(PyArrayObject *
   __pyx_r = PyArray_SIZE(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":278
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":278
  * 
  *         @property
  *         cdef inline npy_intp size(self) nogil:             # <<<<<<<<<<<<<<
@@ -3599,7 +4058,7 @@ static CYTHON_INLINE npy_intp __pyx_f_5numpy_7ndarray_4size_size(PyArrayObject *
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":284
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":284
  * 
  *         @property
  *         cdef inline char* data(self) nogil:             # <<<<<<<<<<<<<<
@@ -3610,7 +4069,7 @@ static CYTHON_INLINE npy_intp __pyx_f_5numpy_7ndarray_4size_size(PyArrayObject *
 static CYTHON_INLINE char *__pyx_f_5numpy_7ndarray_4data_data(PyArrayObject *__pyx_v_self) {
   char *__pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":290
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":290
  *             of `PyArray_DATA()` instead, which returns a 'void*'.
  *             """
  *             return PyArray_BYTES(self)             # <<<<<<<<<<<<<<
@@ -3620,7 +4079,7 @@ static CYTHON_INLINE char *__pyx_f_5numpy_7ndarray_4data_data(PyArrayObject *__p
   __pyx_r = PyArray_BYTES(__pyx_v_self);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":284
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":284
  * 
  *         @property
  *         cdef inline char* data(self) nogil:             # <<<<<<<<<<<<<<
@@ -3633,7 +4092,7 @@ static CYTHON_INLINE char *__pyx_f_5numpy_7ndarray_4data_data(PyArrayObject *__p
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":776
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":776
  * ctypedef npy_cdouble     complex_t
  * 
  * cdef inline object PyArray_MultiIterNew1(a):             # <<<<<<<<<<<<<<
@@ -3650,7 +4109,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew1(PyObject *__
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("PyArray_MultiIterNew1", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":777
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":777
  * 
  * cdef inline object PyArray_MultiIterNew1(a):
  *     return PyArray_MultiIterNew(1, <void*>a)             # <<<<<<<<<<<<<<
@@ -3664,7 +4123,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew1(PyObject *__
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":776
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":776
  * ctypedef npy_cdouble     complex_t
  * 
  * cdef inline object PyArray_MultiIterNew1(a):             # <<<<<<<<<<<<<<
@@ -3683,7 +4142,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew1(PyObject *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":779
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":779
  *     return PyArray_MultiIterNew(1, <void*>a)
  * 
  * cdef inline object PyArray_MultiIterNew2(a, b):             # <<<<<<<<<<<<<<
@@ -3700,7 +4159,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew2(PyObject *__
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("PyArray_MultiIterNew2", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":780
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":780
  * 
  * cdef inline object PyArray_MultiIterNew2(a, b):
  *     return PyArray_MultiIterNew(2, <void*>a, <void*>b)             # <<<<<<<<<<<<<<
@@ -3714,7 +4173,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew2(PyObject *__
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":779
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":779
  *     return PyArray_MultiIterNew(1, <void*>a)
  * 
  * cdef inline object PyArray_MultiIterNew2(a, b):             # <<<<<<<<<<<<<<
@@ -3733,7 +4192,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew2(PyObject *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":782
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":782
  *     return PyArray_MultiIterNew(2, <void*>a, <void*>b)
  * 
  * cdef inline object PyArray_MultiIterNew3(a, b, c):             # <<<<<<<<<<<<<<
@@ -3750,7 +4209,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew3(PyObject *__
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("PyArray_MultiIterNew3", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":783
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":783
  * 
  * cdef inline object PyArray_MultiIterNew3(a, b, c):
  *     return PyArray_MultiIterNew(3, <void*>a, <void*>b, <void*> c)             # <<<<<<<<<<<<<<
@@ -3764,7 +4223,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew3(PyObject *__
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":782
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":782
  *     return PyArray_MultiIterNew(2, <void*>a, <void*>b)
  * 
  * cdef inline object PyArray_MultiIterNew3(a, b, c):             # <<<<<<<<<<<<<<
@@ -3783,7 +4242,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew3(PyObject *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":785
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":785
  *     return PyArray_MultiIterNew(3, <void*>a, <void*>b, <void*> c)
  * 
  * cdef inline object PyArray_MultiIterNew4(a, b, c, d):             # <<<<<<<<<<<<<<
@@ -3800,7 +4259,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew4(PyObject *__
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("PyArray_MultiIterNew4", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":786
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":786
  * 
  * cdef inline object PyArray_MultiIterNew4(a, b, c, d):
  *     return PyArray_MultiIterNew(4, <void*>a, <void*>b, <void*>c, <void*> d)             # <<<<<<<<<<<<<<
@@ -3814,7 +4273,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew4(PyObject *__
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":785
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":785
  *     return PyArray_MultiIterNew(3, <void*>a, <void*>b, <void*> c)
  * 
  * cdef inline object PyArray_MultiIterNew4(a, b, c, d):             # <<<<<<<<<<<<<<
@@ -3833,7 +4292,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew4(PyObject *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":788
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":788
  *     return PyArray_MultiIterNew(4, <void*>a, <void*>b, <void*>c, <void*> d)
  * 
  * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e):             # <<<<<<<<<<<<<<
@@ -3850,7 +4309,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew5(PyObject *__
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("PyArray_MultiIterNew5", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":789
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":789
  * 
  * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e):
  *     return PyArray_MultiIterNew(5, <void*>a, <void*>b, <void*>c, <void*> d, <void*> e)             # <<<<<<<<<<<<<<
@@ -3864,7 +4323,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew5(PyObject *__
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":788
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":788
  *     return PyArray_MultiIterNew(4, <void*>a, <void*>b, <void*>c, <void*> d)
  * 
  * cdef inline object PyArray_MultiIterNew5(a, b, c, d, e):             # <<<<<<<<<<<<<<
@@ -3883,7 +4342,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyArray_MultiIterNew5(PyObject *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":791
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":791
  *     return PyArray_MultiIterNew(5, <void*>a, <void*>b, <void*>c, <void*> d, <void*> e)
  * 
  * cdef inline tuple PyDataType_SHAPE(dtype d):             # <<<<<<<<<<<<<<
@@ -3897,7 +4356,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
   int __pyx_t_1;
   __Pyx_RefNannySetupContext("PyDataType_SHAPE", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":792
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":792
  * 
  * cdef inline tuple PyDataType_SHAPE(dtype d):
  *     if PyDataType_HASSUBARRAY(d):             # <<<<<<<<<<<<<<
@@ -3907,7 +4366,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
   __pyx_t_1 = PyDataType_HASSUBARRAY(__pyx_v_d);
   if (__pyx_t_1) {
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":793
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":793
  * cdef inline tuple PyDataType_SHAPE(dtype d):
  *     if PyDataType_HASSUBARRAY(d):
  *         return <tuple>d.subarray.shape             # <<<<<<<<<<<<<<
@@ -3919,7 +4378,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
     __pyx_r = ((PyObject*)__pyx_v_d->subarray->shape);
     goto __pyx_L0;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":792
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":792
  * 
  * cdef inline tuple PyDataType_SHAPE(dtype d):
  *     if PyDataType_HASSUBARRAY(d):             # <<<<<<<<<<<<<<
@@ -3928,7 +4387,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
  */
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":795
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":795
  *         return <tuple>d.subarray.shape
  *     else:
  *         return ()             # <<<<<<<<<<<<<<
@@ -3942,7 +4401,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
     goto __pyx_L0;
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":791
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":791
  *     return PyArray_MultiIterNew(5, <void*>a, <void*>b, <void*>c, <void*> d, <void*> e)
  * 
  * cdef inline tuple PyDataType_SHAPE(dtype d):             # <<<<<<<<<<<<<<
@@ -3957,7 +4416,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":970
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":970
  *     int _import_umath() except -1
  * 
  * cdef inline void set_array_base(ndarray arr, object base):             # <<<<<<<<<<<<<<
@@ -3967,7 +4426,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_PyDataType_SHAPE(PyArray_Descr *__
 
 static CYTHON_INLINE void __pyx_f_5numpy_set_array_base(PyArrayObject *__pyx_v_arr, PyObject *__pyx_v_base) {
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":971
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":971
  * 
  * cdef inline void set_array_base(ndarray arr, object base):
  *     Py_INCREF(base) # important to do this before stealing the reference below!             # <<<<<<<<<<<<<<
@@ -3976,7 +4435,7 @@ static CYTHON_INLINE void __pyx_f_5numpy_set_array_base(PyArrayObject *__pyx_v_a
  */
   Py_INCREF(__pyx_v_base);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":972
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":972
  * cdef inline void set_array_base(ndarray arr, object base):
  *     Py_INCREF(base) # important to do this before stealing the reference below!
  *     PyArray_SetBaseObject(arr, base)             # <<<<<<<<<<<<<<
@@ -3985,7 +4444,7 @@ static CYTHON_INLINE void __pyx_f_5numpy_set_array_base(PyArrayObject *__pyx_v_a
  */
   (void)(PyArray_SetBaseObject(__pyx_v_arr, __pyx_v_base));
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":970
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":970
  *     int _import_umath() except -1
  * 
  * cdef inline void set_array_base(ndarray arr, object base):             # <<<<<<<<<<<<<<
@@ -3996,7 +4455,7 @@ static CYTHON_INLINE void __pyx_f_5numpy_set_array_base(PyArrayObject *__pyx_v_a
   /* function exit code */
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":974
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":974
  *     PyArray_SetBaseObject(arr, base)
  * 
  * cdef inline object get_array_base(ndarray arr):             # <<<<<<<<<<<<<<
@@ -4011,7 +4470,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
   int __pyx_t_1;
   __Pyx_RefNannySetupContext("get_array_base", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":975
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":975
  * 
  * cdef inline object get_array_base(ndarray arr):
  *     base = PyArray_BASE(arr)             # <<<<<<<<<<<<<<
@@ -4020,7 +4479,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
  */
   __pyx_v_base = PyArray_BASE(__pyx_v_arr);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":976
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":976
  * cdef inline object get_array_base(ndarray arr):
  *     base = PyArray_BASE(arr)
  *     if base is NULL:             # <<<<<<<<<<<<<<
@@ -4030,7 +4489,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
   __pyx_t_1 = (__pyx_v_base == NULL);
   if (__pyx_t_1) {
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":977
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":977
  *     base = PyArray_BASE(arr)
  *     if base is NULL:
  *         return None             # <<<<<<<<<<<<<<
@@ -4041,7 +4500,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
     __pyx_r = Py_None; __Pyx_INCREF(Py_None);
     goto __pyx_L0;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":976
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":976
  * cdef inline object get_array_base(ndarray arr):
  *     base = PyArray_BASE(arr)
  *     if base is NULL:             # <<<<<<<<<<<<<<
@@ -4050,7 +4509,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
  */
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":978
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":978
  *     if base is NULL:
  *         return None
  *     return <object>base             # <<<<<<<<<<<<<<
@@ -4062,7 +4521,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
   __pyx_r = ((PyObject *)__pyx_v_base);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":974
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":974
  *     PyArray_SetBaseObject(arr, base)
  * 
  * cdef inline object get_array_base(ndarray arr):             # <<<<<<<<<<<<<<
@@ -4077,7 +4536,7 @@ static CYTHON_INLINE PyObject *__pyx_f_5numpy_get_array_base(PyArrayObject *__py
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":982
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":982
  * # Versions of the import_* functions which are more suitable for
  * # Cython code.
  * cdef inline int import_array() except -1:             # <<<<<<<<<<<<<<
@@ -4101,7 +4560,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("import_array", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
  * # Cython code.
  * cdef inline int import_array() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4117,7 +4576,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
     __Pyx_XGOTREF(__pyx_t_3);
     /*try:*/ {
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":984
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":984
  * cdef inline int import_array() except -1:
  *     try:
  *         __pyx_import_array()             # <<<<<<<<<<<<<<
@@ -4126,7 +4585,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
  */
       __pyx_t_4 = _import_array(); if (unlikely(__pyx_t_4 == ((int)-1))) __PYX_ERR(1, 984, __pyx_L3_error)
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
  * # Cython code.
  * cdef inline int import_array() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4140,7 +4599,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
     goto __pyx_L8_try_end;
     __pyx_L3_error:;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":985
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":985
  *     try:
  *         __pyx_import_array()
  *     except Exception:             # <<<<<<<<<<<<<<
@@ -4155,7 +4614,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
       __Pyx_XGOTREF(__pyx_t_6);
       __Pyx_XGOTREF(__pyx_t_7);
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":986
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":986
  *         __pyx_import_array()
  *     except Exception:
  *         raise ImportError("numpy.core.multiarray failed to import")             # <<<<<<<<<<<<<<
@@ -4170,7 +4629,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
     }
     goto __pyx_L5_except_error;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":983
  * # Cython code.
  * cdef inline int import_array() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4186,7 +4645,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
     __pyx_L8_try_end:;
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":982
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":982
  * # Versions of the import_* functions which are more suitable for
  * # Cython code.
  * cdef inline int import_array() except -1:             # <<<<<<<<<<<<<<
@@ -4209,7 +4668,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_array(void) {
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":988
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":988
  *         raise ImportError("numpy.core.multiarray failed to import")
  * 
  * cdef inline int import_umath() except -1:             # <<<<<<<<<<<<<<
@@ -4233,7 +4692,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("import_umath", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
  * 
  * cdef inline int import_umath() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4249,7 +4708,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
     __Pyx_XGOTREF(__pyx_t_3);
     /*try:*/ {
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":990
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":990
  * cdef inline int import_umath() except -1:
  *     try:
  *         _import_umath()             # <<<<<<<<<<<<<<
@@ -4258,7 +4717,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
  */
       __pyx_t_4 = _import_umath(); if (unlikely(__pyx_t_4 == ((int)-1))) __PYX_ERR(1, 990, __pyx_L3_error)
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
  * 
  * cdef inline int import_umath() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4272,7 +4731,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
     goto __pyx_L8_try_end;
     __pyx_L3_error:;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":991
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":991
  *     try:
  *         _import_umath()
  *     except Exception:             # <<<<<<<<<<<<<<
@@ -4287,7 +4746,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
       __Pyx_XGOTREF(__pyx_t_6);
       __Pyx_XGOTREF(__pyx_t_7);
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":992
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":992
  *         _import_umath()
  *     except Exception:
  *         raise ImportError("numpy.core.umath failed to import")             # <<<<<<<<<<<<<<
@@ -4302,7 +4761,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
     }
     goto __pyx_L5_except_error;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":989
  * 
  * cdef inline int import_umath() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4318,7 +4777,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
     __pyx_L8_try_end:;
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":988
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":988
  *         raise ImportError("numpy.core.multiarray failed to import")
  * 
  * cdef inline int import_umath() except -1:             # <<<<<<<<<<<<<<
@@ -4341,7 +4800,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_umath(void) {
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":994
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":994
  *         raise ImportError("numpy.core.umath failed to import")
  * 
  * cdef inline int import_ufunc() except -1:             # <<<<<<<<<<<<<<
@@ -4365,7 +4824,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("import_ufunc", 1);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
  * 
  * cdef inline int import_ufunc() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4381,7 +4840,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
     __Pyx_XGOTREF(__pyx_t_3);
     /*try:*/ {
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":996
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":996
  * cdef inline int import_ufunc() except -1:
  *     try:
  *         _import_umath()             # <<<<<<<<<<<<<<
@@ -4390,7 +4849,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
  */
       __pyx_t_4 = _import_umath(); if (unlikely(__pyx_t_4 == ((int)-1))) __PYX_ERR(1, 996, __pyx_L3_error)
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
  * 
  * cdef inline int import_ufunc() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4404,7 +4863,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
     goto __pyx_L8_try_end;
     __pyx_L3_error:;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":997
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":997
  *     try:
  *         _import_umath()
  *     except Exception:             # <<<<<<<<<<<<<<
@@ -4419,7 +4878,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
       __Pyx_XGOTREF(__pyx_t_6);
       __Pyx_XGOTREF(__pyx_t_7);
 
-      /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":998
+      /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":998
  *         _import_umath()
  *     except Exception:
  *         raise ImportError("numpy.core.umath failed to import")             # <<<<<<<<<<<<<<
@@ -4434,7 +4893,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
     }
     goto __pyx_L5_except_error;
 
-    /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
+    /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":995
  * 
  * cdef inline int import_ufunc() except -1:
  *     try:             # <<<<<<<<<<<<<<
@@ -4450,7 +4909,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
     __pyx_L8_try_end:;
   }
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":994
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":994
  *         raise ImportError("numpy.core.umath failed to import")
  * 
  * cdef inline int import_ufunc() except -1:             # <<<<<<<<<<<<<<
@@ -4473,7 +4932,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1001
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1001
  * 
  * 
  * cdef inline bint is_timedelta64_object(object obj):             # <<<<<<<<<<<<<<
@@ -4484,7 +4943,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_import_ufunc(void) {
 static CYTHON_INLINE int __pyx_f_5numpy_is_timedelta64_object(PyObject *__pyx_v_obj) {
   int __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1013
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1013
  *     bool
  *     """
  *     return PyObject_TypeCheck(obj, &PyTimedeltaArrType_Type)             # <<<<<<<<<<<<<<
@@ -4494,7 +4953,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_timedelta64_object(PyObject *__pyx_v_
   __pyx_r = PyObject_TypeCheck(__pyx_v_obj, (&PyTimedeltaArrType_Type));
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1001
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1001
  * 
  * 
  * cdef inline bint is_timedelta64_object(object obj):             # <<<<<<<<<<<<<<
@@ -4507,7 +4966,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_timedelta64_object(PyObject *__pyx_v_
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1016
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1016
  * 
  * 
  * cdef inline bint is_datetime64_object(object obj):             # <<<<<<<<<<<<<<
@@ -4518,7 +4977,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_timedelta64_object(PyObject *__pyx_v_
 static CYTHON_INLINE int __pyx_f_5numpy_is_datetime64_object(PyObject *__pyx_v_obj) {
   int __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1028
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1028
  *     bool
  *     """
  *     return PyObject_TypeCheck(obj, &PyDatetimeArrType_Type)             # <<<<<<<<<<<<<<
@@ -4528,7 +4987,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_datetime64_object(PyObject *__pyx_v_o
   __pyx_r = PyObject_TypeCheck(__pyx_v_obj, (&PyDatetimeArrType_Type));
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1016
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1016
  * 
  * 
  * cdef inline bint is_datetime64_object(object obj):             # <<<<<<<<<<<<<<
@@ -4541,7 +5000,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_datetime64_object(PyObject *__pyx_v_o
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1031
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1031
  * 
  * 
  * cdef inline npy_datetime get_datetime64_value(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4552,7 +5011,7 @@ static CYTHON_INLINE int __pyx_f_5numpy_is_datetime64_object(PyObject *__pyx_v_o
 static CYTHON_INLINE npy_datetime __pyx_f_5numpy_get_datetime64_value(PyObject *__pyx_v_obj) {
   npy_datetime __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1038
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1038
  *     also needed.  That can be found using `get_datetime64_unit`.
  *     """
  *     return (<PyDatetimeScalarObject*>obj).obval             # <<<<<<<<<<<<<<
@@ -4562,7 +5021,7 @@ static CYTHON_INLINE npy_datetime __pyx_f_5numpy_get_datetime64_value(PyObject *
   __pyx_r = ((PyDatetimeScalarObject *)__pyx_v_obj)->obval;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1031
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1031
  * 
  * 
  * cdef inline npy_datetime get_datetime64_value(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4575,7 +5034,7 @@ static CYTHON_INLINE npy_datetime __pyx_f_5numpy_get_datetime64_value(PyObject *
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1041
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1041
  * 
  * 
  * cdef inline npy_timedelta get_timedelta64_value(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4586,7 +5045,7 @@ static CYTHON_INLINE npy_datetime __pyx_f_5numpy_get_datetime64_value(PyObject *
 static CYTHON_INLINE npy_timedelta __pyx_f_5numpy_get_timedelta64_value(PyObject *__pyx_v_obj) {
   npy_timedelta __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1045
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1045
  *     returns the int64 value underlying scalar numpy timedelta64 object
  *     """
  *     return (<PyTimedeltaScalarObject*>obj).obval             # <<<<<<<<<<<<<<
@@ -4596,7 +5055,7 @@ static CYTHON_INLINE npy_timedelta __pyx_f_5numpy_get_timedelta64_value(PyObject
   __pyx_r = ((PyTimedeltaScalarObject *)__pyx_v_obj)->obval;
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1041
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1041
  * 
  * 
  * cdef inline npy_timedelta get_timedelta64_value(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4609,7 +5068,7 @@ static CYTHON_INLINE npy_timedelta __pyx_f_5numpy_get_timedelta64_value(PyObject
   return __pyx_r;
 }
 
-/* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1048
+/* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1048
  * 
  * 
  * cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4620,7 +5079,7 @@ static CYTHON_INLINE npy_timedelta __pyx_f_5numpy_get_timedelta64_value(PyObject
 static CYTHON_INLINE NPY_DATETIMEUNIT __pyx_f_5numpy_get_datetime64_unit(PyObject *__pyx_v_obj) {
   NPY_DATETIMEUNIT __pyx_r;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1052
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1052
  *     returns the unit part of the dtype for a numpy datetime64 object.
  *     """
  *     return <NPY_DATETIMEUNIT>(<PyDatetimeScalarObject*>obj).obmeta.base             # <<<<<<<<<<<<<<
@@ -4628,7 +5087,7 @@ static CYTHON_INLINE NPY_DATETIMEUNIT __pyx_f_5numpy_get_datetime64_unit(PyObjec
   __pyx_r = ((NPY_DATETIMEUNIT)((PyDatetimeScalarObject *)__pyx_v_obj)->obmeta.base);
   goto __pyx_L0;
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1048
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":1048
  * 
  * 
  * cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil:             # <<<<<<<<<<<<<<
@@ -4641,7 +5100,7 @@ static CYTHON_INLINE NPY_DATETIMEUNIT __pyx_f_5numpy_get_datetime64_unit(PyObjec
   return __pyx_r;
 }
 
-/* "_openjpeg.pyx":38
+/* "_openjpeg.pyx":52
  * 
  * 
  * def get_version() -> bytes:             # <<<<<<<<<<<<<<
@@ -4676,7 +5135,7 @@ static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_s
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("get_version", 1);
 
-  /* "_openjpeg.pyx":40
+  /* "_openjpeg.pyx":54
  * def get_version() -> bytes:
  *     """Return the openjpeg version as bytes."""
  *     cdef char *version = OpenJpegVersion()             # <<<<<<<<<<<<<<
@@ -4685,7 +5144,7 @@ static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_s
  */
   __pyx_v_version = OpenJpegVersion();
 
-  /* "_openjpeg.pyx":42
+  /* "_openjpeg.pyx":56
  *     cdef char *version = OpenJpegVersion()
  * 
  *     return version             # <<<<<<<<<<<<<<
@@ -4693,13 +5152,13 @@ static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_s
  * 
  */
   __Pyx_XDECREF(__pyx_r);
-  __pyx_t_1 = __Pyx_PyBytes_FromString(__pyx_v_version); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 42, __pyx_L1_error)
+  __pyx_t_1 = __Pyx_PyBytes_FromString(__pyx_v_version); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 56, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
   __pyx_r = ((PyObject*)__pyx_t_1);
   __pyx_t_1 = 0;
   goto __pyx_L0;
 
-  /* "_openjpeg.pyx":38
+  /* "_openjpeg.pyx":52
  * 
  * 
  * def get_version() -> bytes:             # <<<<<<<<<<<<<<
@@ -4718,7 +5177,7 @@ static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_s
   return __pyx_r;
 }
 
-/* "_openjpeg.pyx":45
+/* "_openjpeg.pyx":59
  * 
  * 
  * def decode(             # <<<<<<<<<<<<<<
@@ -4726,7 +5185,7 @@ static PyObject *__pyx_pf_9_openjpeg_get_version(CYTHON_UNUSED PyObject *__pyx_s
  *     codec: int = 0,
  */
 
-static PyObject *__pyx_pf_9_openjpeg_6__defaults__(CYTHON_UNUSED PyObject *__pyx_self) {
+static PyObject *__pyx_pf_9_openjpeg_8__defaults__(CYTHON_UNUSED PyObject *__pyx_self) {
   PyObject *__pyx_r = NULL;
   __Pyx_RefNannyDeclarations
   PyObject *__pyx_t_1 = NULL;
@@ -4737,36 +5196,36 @@ static PyObject *__pyx_pf_9_openjpeg_6__defaults__(CYTHON_UNUSED PyObject *__pyx
   __Pyx_RefNannySetupContext("__defaults__", 1);
   __Pyx_XDECREF(__pyx_r);
 
-  /* "_openjpeg.pyx":48
+  /* "_openjpeg.pyx":62
  *     fp: BinaryIO,
  *     codec: int = 0,
  *     as_array: bool = False             # <<<<<<<<<<<<<<
  * ) -> Union[np.ndarray, bytearray]:
  *     """Return the decoded JPEG 2000 data from Python file-like `fp`.
  */
-  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 45, __pyx_L1_error)
+  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 59, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
   __Pyx_INCREF(__Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_self)->__pyx_arg_codec);
   __Pyx_GIVEREF(__Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_self)->__pyx_arg_codec);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_self)->__pyx_arg_codec)) __PYX_ERR(0, 45, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_self)->__pyx_arg_codec)) __PYX_ERR(0, 59, __pyx_L1_error);
   __Pyx_INCREF(((PyObject *)Py_False));
   __Pyx_GIVEREF(((PyObject *)Py_False));
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, ((PyObject *)Py_False))) __PYX_ERR(0, 45, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, ((PyObject *)Py_False))) __PYX_ERR(0, 59, __pyx_L1_error);
 
-  /* "_openjpeg.pyx":45
+  /* "_openjpeg.pyx":59
  * 
  * 
  * def decode(             # <<<<<<<<<<<<<<
  *     fp: BinaryIO,
  *     codec: int = 0,
  */
-  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
+  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 59, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
   __Pyx_GIVEREF(__pyx_t_1);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1)) __PYX_ERR(0, 45, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1)) __PYX_ERR(0, 59, __pyx_L1_error);
   __Pyx_INCREF(Py_None);
   __Pyx_GIVEREF(Py_None);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, Py_None)) __PYX_ERR(0, 45, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, Py_None)) __PYX_ERR(0, 59, __pyx_L1_error);
   __pyx_t_1 = 0;
   __pyx_r = __pyx_t_2;
   __pyx_t_2 = 0;
@@ -4847,26 +5306,26 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
           (void)__Pyx_Arg_NewRef_FASTCALL(values[0]);
           kw_args--;
         }
-        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 45, __pyx_L3_error)
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 59, __pyx_L3_error)
         else goto __pyx_L5_argtuple_error;
         CYTHON_FALLTHROUGH;
         case  1:
         if (kw_args > 0) {
           PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_codec);
           if (value) { values[1] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; }
-          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 45, __pyx_L3_error)
+          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 59, __pyx_L3_error)
         }
         CYTHON_FALLTHROUGH;
         case  2:
         if (kw_args > 0) {
           PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_as_array);
           if (value) { values[2] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; }
-          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 45, __pyx_L3_error)
+          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 59, __pyx_L3_error)
         }
       }
       if (unlikely(kw_args > 0)) {
         const Py_ssize_t kwd_pos_args = __pyx_nargs;
-        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "decode") < 0)) __PYX_ERR(0, 45, __pyx_L3_error)
+        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "decode") < 0)) __PYX_ERR(0, 59, __pyx_L3_error)
       }
     } else {
       switch (__pyx_nargs) {
@@ -4885,7 +5344,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
   }
   goto __pyx_L6_skip;
   __pyx_L5_argtuple_error:;
-  __Pyx_RaiseArgtupleInvalid("decode", 0, 1, 3, __pyx_nargs); __PYX_ERR(0, 45, __pyx_L3_error)
+  __Pyx_RaiseArgtupleInvalid("decode", 0, 1, 3, __pyx_nargs); __PYX_ERR(0, 59, __pyx_L3_error)
   __pyx_L6_skip:;
   goto __pyx_L4_argument_unpacking_done;
   __pyx_L3_error:;
@@ -4899,7 +5358,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
   __Pyx_RefNannyFinishContext();
   return NULL;
   __pyx_L4_argument_unpacking_done:;
-  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_codec), (&PyInt_Type), 0, "codec", 1))) __PYX_ERR(0, 47, __pyx_L1_error)
+  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_codec), (&PyInt_Type), 0, "codec", 1))) __PYX_ERR(0, 61, __pyx_L1_error)
   __pyx_r = __pyx_pf_9_openjpeg_2decode(__pyx_self, __pyx_v_fp, __pyx_v_codec, __pyx_v_as_array);
 
   /* function exit code */
@@ -4924,7 +5383,7 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
   PyObject *__pyx_v_p_in;
   unsigned char *__pyx_v_p_out;
   PyObject *__pyx_v_out = NULL;
-  int __pyx_v_result;
+  int __pyx_v_return_code;
   PyObject *__pyx_r = NULL;
   __Pyx_RefNannyDeclarations
   PyObject *__pyx_t_1 = NULL;
@@ -4940,14 +5399,14 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("decode", 1);
 
-  /* "_openjpeg.pyx":79
+  /* "_openjpeg.pyx":93
  *         If unable to decode the JPEG 2000 data.
  *     """
  *     param = get_parameters(fp, codec)             # <<<<<<<<<<<<<<
  *     bpp = ceil(param['precision'] / 8)
- *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
+ *     if bpp == 3:
  */
-  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_get_parameters); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 79, __pyx_L1_error)
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_get_parameters); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 93, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
   __pyx_t_3 = NULL;
   __pyx_t_4 = 0;
@@ -4967,25 +5426,25 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
     PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_fp, __pyx_v_codec};
     __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_2, __pyx_callargs+1-__pyx_t_4, 2+__pyx_t_4);
     __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
-    if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 79, __pyx_L1_error)
+    if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 93, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_1);
     __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
   }
   __pyx_v_param = __pyx_t_1;
   __pyx_t_1 = 0;
 
-  /* "_openjpeg.pyx":80
+  /* "_openjpeg.pyx":94
  *     """
  *     param = get_parameters(fp, codec)
  *     bpp = ceil(param['precision'] / 8)             # <<<<<<<<<<<<<<
- *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
- * 
+ *     if bpp == 3:
+ *         bpp = 4
  */
-  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_ceil); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 80, __pyx_L1_error)
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_ceil); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 94, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_3 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_precision); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 80, __pyx_L1_error)
+  __pyx_t_3 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_precision); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 94, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_3);
-  __pyx_t_5 = __Pyx_PyInt_TrueDivideObjC(__pyx_t_3, __pyx_int_8, 8, 0, 0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 80, __pyx_L1_error)
+  __pyx_t_5 = __Pyx_PyInt_TrueDivideObjC(__pyx_t_3, __pyx_int_8, 8, 0, 0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 94, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_5);
   __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
   __pyx_t_3 = NULL;
@@ -5007,41 +5466,70 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
     __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_2, __pyx_callargs+1-__pyx_t_4, 1+__pyx_t_4);
     __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
     __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
-    if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 80, __pyx_L1_error)
+    if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 94, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_1);
     __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
   }
   __pyx_v_bpp = __pyx_t_1;
   __pyx_t_1 = 0;
 
-  /* "_openjpeg.pyx":81
+  /* "_openjpeg.pyx":95
+ *     param = get_parameters(fp, codec)
+ *     bpp = ceil(param['precision'] / 8)
+ *     if bpp == 3:             # <<<<<<<<<<<<<<
+ *         bpp = 4
+ *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
+ */
+  __pyx_t_6 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_bpp, __pyx_int_3, 3, 0)); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 95, __pyx_L1_error)
+  if (__pyx_t_6) {
+
+    /* "_openjpeg.pyx":96
+ *     bpp = ceil(param['precision'] / 8)
+ *     if bpp == 3:
+ *         bpp = 4             # <<<<<<<<<<<<<<
+ *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
+ * 
+ */
+    __Pyx_INCREF(__pyx_int_4);
+    __Pyx_DECREF_SET(__pyx_v_bpp, __pyx_int_4);
+
+    /* "_openjpeg.pyx":95
  *     param = get_parameters(fp, codec)
  *     bpp = ceil(param['precision'] / 8)
+ *     if bpp == 3:             # <<<<<<<<<<<<<<
+ *         bpp = 4
+ *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
+ */
+  }
+
+  /* "_openjpeg.pyx":97
+ *     if bpp == 3:
+ *         bpp = 4
  *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp             # <<<<<<<<<<<<<<
  * 
  *     cdef PyObject* p_in = <PyObject*>fp
  */
-  __pyx_t_1 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_rows); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_1 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_rows); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_columns); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_columns); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_5);
   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_nr_components); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_param, __pyx_n_u_nr_components); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_1 = PyNumber_Multiply(__pyx_t_5, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_1 = PyNumber_Multiply(__pyx_t_5, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
   __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_bpp); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 81, __pyx_L1_error)
+  __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_bpp); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 97, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
   __pyx_v_nr_bytes = __pyx_t_2;
   __pyx_t_2 = 0;
 
-  /* "_openjpeg.pyx":83
+  /* "_openjpeg.pyx":99
  *     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
  * 
  *     cdef PyObject* p_in = <PyObject*>fp             # <<<<<<<<<<<<<<
@@ -5050,43 +5538,43 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
  */
   __pyx_v_p_in = ((PyObject *)__pyx_v_fp);
 
-  /* "_openjpeg.pyx":85
+  /* "_openjpeg.pyx":101
  *     cdef PyObject* p_in = <PyObject*>fp
  *     cdef unsigned char *p_out
  *     if as_array:             # <<<<<<<<<<<<<<
  *         out = np.zeros(nr_bytes, dtype=np.uint8)
- *         p_out = <unsigned char *>np.PyArray_DATA(out)
+ *         p_out = <unsigned char *>cnp.PyArray_DATA(out)
  */
-  __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_v_as_array); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 85, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_v_as_array); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 101, __pyx_L1_error)
   if (__pyx_t_6) {
 
-    /* "_openjpeg.pyx":86
+    /* "_openjpeg.pyx":102
  *     cdef unsigned char *p_out
  *     if as_array:
  *         out = np.zeros(nr_bytes, dtype=np.uint8)             # <<<<<<<<<<<<<<
- *         p_out = <unsigned char *>np.PyArray_DATA(out)
+ *         p_out = <unsigned char *>cnp.PyArray_DATA(out)
  *     else:
  */
-    __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_2);
-    __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_zeros); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_zeros); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_1);
     __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-    __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_2);
     __Pyx_INCREF(__pyx_v_nr_bytes);
     __Pyx_GIVEREF(__pyx_v_nr_bytes);
-    if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_nr_bytes)) __PYX_ERR(0, 86, __pyx_L1_error);
-    __pyx_t_5 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 86, __pyx_L1_error)
+    if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_nr_bytes)) __PYX_ERR(0, 102, __pyx_L1_error);
+    __pyx_t_5 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_5);
-    __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_np); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_3);
-    __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_uint8); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_uint8); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_7);
     __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
-    if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_dtype, __pyx_t_7) < 0) __PYX_ERR(0, 86, __pyx_L1_error)
+    if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_dtype, __pyx_t_7) < 0) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-    __pyx_t_7 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_2, __pyx_t_5); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 86, __pyx_L1_error)
+    __pyx_t_7 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_2, __pyx_t_5); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 102, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_7);
     __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
     __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
@@ -5094,154 +5582,84 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
     __pyx_v_out = __pyx_t_7;
     __pyx_t_7 = 0;
 
-    /* "_openjpeg.pyx":87
+    /* "_openjpeg.pyx":103
  *     if as_array:
  *         out = np.zeros(nr_bytes, dtype=np.uint8)
- *         p_out = <unsigned char *>np.PyArray_DATA(out)             # <<<<<<<<<<<<<<
+ *         p_out = <unsigned char *>cnp.PyArray_DATA(out)             # <<<<<<<<<<<<<<
  *     else:
  *         out = bytearray(nr_bytes)
  */
-    if (!(likely(((__pyx_v_out) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_out, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 87, __pyx_L1_error)
+    if (!(likely(((__pyx_v_out) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_out, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 103, __pyx_L1_error)
     __pyx_v_p_out = ((unsigned char *)PyArray_DATA(((PyArrayObject *)__pyx_v_out)));
 
-    /* "_openjpeg.pyx":85
+    /* "_openjpeg.pyx":101
  *     cdef PyObject* p_in = <PyObject*>fp
  *     cdef unsigned char *p_out
  *     if as_array:             # <<<<<<<<<<<<<<
  *         out = np.zeros(nr_bytes, dtype=np.uint8)
- *         p_out = <unsigned char *>np.PyArray_DATA(out)
+ *         p_out = <unsigned char *>cnp.PyArray_DATA(out)
  */
-    goto __pyx_L3;
+    goto __pyx_L4;
   }
 
-  /* "_openjpeg.pyx":89
- *         p_out = <unsigned char *>np.PyArray_DATA(out)
+  /* "_openjpeg.pyx":105
+ *         p_out = <unsigned char *>cnp.PyArray_DATA(out)
  *     else:
  *         out = bytearray(nr_bytes)             # <<<<<<<<<<<<<<
  *         p_out = <unsigned char *>out
  * 
  */
   /*else*/ {
-    __pyx_t_7 = __Pyx_PyObject_CallOneArg(((PyObject *)(&PyByteArray_Type)), __pyx_v_nr_bytes); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 89, __pyx_L1_error)
+    __pyx_t_7 = __Pyx_PyObject_CallOneArg(((PyObject *)(&PyByteArray_Type)), __pyx_v_nr_bytes); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 105, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_7);
     __pyx_v_out = __pyx_t_7;
     __pyx_t_7 = 0;
 
-    /* "_openjpeg.pyx":90
+    /* "_openjpeg.pyx":106
  *     else:
  *         out = bytearray(nr_bytes)
  *         p_out = <unsigned char *>out             # <<<<<<<<<<<<<<
  * 
- *     result = Decode(p_in, p_out, codec)
+ *     return_code = Decode(p_in, p_out, codec)
  */
-    __pyx_t_8 = __Pyx_PyObject_AsWritableUString(__pyx_v_out); if (unlikely((!__pyx_t_8) && PyErr_Occurred())) __PYX_ERR(0, 90, __pyx_L1_error)
+    __pyx_t_8 = __Pyx_PyObject_AsWritableUString(__pyx_v_out); if (unlikely((!__pyx_t_8) && PyErr_Occurred())) __PYX_ERR(0, 106, __pyx_L1_error)
     __pyx_v_p_out = ((unsigned char *)__pyx_t_8);
   }
-  __pyx_L3:;
+  __pyx_L4:;
 
-  /* "_openjpeg.pyx":92
+  /* "_openjpeg.pyx":108
  *         p_out = <unsigned char *>out
  * 
- *     result = Decode(p_in, p_out, codec)             # <<<<<<<<<<<<<<
- *     if result != 0:
- *         raise RuntimeError(
- */
-  __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_codec); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L1_error)
-  __pyx_v_result = Decode(__pyx_v_p_in, __pyx_v_p_out, __pyx_t_4);
-
-  /* "_openjpeg.pyx":93
- * 
- *     result = Decode(p_in, p_out, codec)
- *     if result != 0:             # <<<<<<<<<<<<<<
- *         raise RuntimeError(
- *             f"Error decoding the J2K data: {ERRORS.get(result, result)}"
- */
-  __pyx_t_6 = (__pyx_v_result != 0);
-  if (unlikely(__pyx_t_6)) {
-
-    /* "_openjpeg.pyx":95
- *     if result != 0:
- *         raise RuntimeError(
- *             f"Error decoding the J2K data: {ERRORS.get(result, result)}"             # <<<<<<<<<<<<<<
- *         )
- * 
- */
-    __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_n_s_ERRORS); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_5);
-    __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_get); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_2);
-    __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
-    __pyx_t_5 = __Pyx_PyInt_From_int(__pyx_v_result); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_5);
-    __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_result); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_1);
-    __pyx_t_3 = NULL;
-    __pyx_t_4 = 0;
-    #if CYTHON_UNPACK_METHODS
-    if (unlikely(PyMethod_Check(__pyx_t_2))) {
-      __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
-      if (likely(__pyx_t_3)) {
-        PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
-        __Pyx_INCREF(__pyx_t_3);
-        __Pyx_INCREF(function);
-        __Pyx_DECREF_SET(__pyx_t_2, function);
-        __pyx_t_4 = 1;
-      }
-    }
-    #endif
-    {
-      PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_t_5, __pyx_t_1};
-      __pyx_t_7 = __Pyx_PyObject_FastCall(__pyx_t_2, __pyx_callargs+1-__pyx_t_4, 2+__pyx_t_4);
-      __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
-      __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 95, __pyx_L1_error)
-      __Pyx_GOTREF(__pyx_t_7);
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-    }
-    __pyx_t_2 = __Pyx_PyObject_FormatSimple(__pyx_t_7, __pyx_empty_unicode); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_2);
-    __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-    __pyx_t_7 = __Pyx_PyUnicode_Concat(__pyx_kp_u_Error_decoding_the_J2K_data, __pyx_t_2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 95, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_7);
-    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-    /* "_openjpeg.pyx":94
- *     result = Decode(p_in, p_out, codec)
- *     if result != 0:
- *         raise RuntimeError(             # <<<<<<<<<<<<<<
- *             f"Error decoding the J2K data: {ERRORS.get(result, result)}"
- *         )
- */
-    __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_RuntimeError, __pyx_t_7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 94, __pyx_L1_error)
-    __Pyx_GOTREF(__pyx_t_2);
-    __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
-    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-    __PYX_ERR(0, 94, __pyx_L1_error)
-
-    /* "_openjpeg.pyx":93
+ *     return_code = Decode(p_in, p_out, codec)             # <<<<<<<<<<<<<<
  * 
- *     result = Decode(p_in, p_out, codec)
- *     if result != 0:             # <<<<<<<<<<<<<<
- *         raise RuntimeError(
- *             f"Error decoding the J2K data: {ERRORS.get(result, result)}"
+ *     return return_code, out
  */
-  }
+  __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_codec); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 108, __pyx_L1_error)
+  __pyx_v_return_code = Decode(__pyx_v_p_in, __pyx_v_p_out, __pyx_t_4);
 
-  /* "_openjpeg.pyx":98
- *         )
+  /* "_openjpeg.pyx":110
+ *     return_code = Decode(p_in, p_out, codec)
  * 
- *     return out             # <<<<<<<<<<<<<<
+ *     return return_code, out             # <<<<<<<<<<<<<<
  * 
  * 
  */
   __Pyx_XDECREF(__pyx_r);
+  __pyx_t_7 = __Pyx_PyInt_From_int(__pyx_v_return_code); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 110, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_7);
+  __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 110, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_5);
+  __Pyx_GIVEREF(__pyx_t_7);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_7)) __PYX_ERR(0, 110, __pyx_L1_error);
   __Pyx_INCREF(__pyx_v_out);
-  __pyx_r = __pyx_v_out;
+  __Pyx_GIVEREF(__pyx_v_out);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_out)) __PYX_ERR(0, 110, __pyx_L1_error);
+  __pyx_t_7 = 0;
+  __pyx_r = __pyx_t_5;
+  __pyx_t_5 = 0;
   goto __pyx_L0;
 
-  /* "_openjpeg.pyx":45
+  /* "_openjpeg.pyx":59
  * 
  * 
  * def decode(             # <<<<<<<<<<<<<<
@@ -5268,7 +5686,7 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
   return __pyx_r;
 }
 
-/* "_openjpeg.pyx":101
+/* "_openjpeg.pyx":113
  * 
  * 
  * def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bool]]:             # <<<<<<<<<<<<<<
@@ -5276,7 +5694,7 @@ static PyObject *__pyx_pf_9_openjpeg_2decode(CYTHON_UNUSED PyObject *__pyx_self,
  * 
  */
 
-static PyObject *__pyx_pf_9_openjpeg_8__defaults__(CYTHON_UNUSED PyObject *__pyx_self) {
+static PyObject *__pyx_pf_9_openjpeg_10__defaults__(CYTHON_UNUSED PyObject *__pyx_self) {
   PyObject *__pyx_r = NULL;
   __Pyx_RefNannyDeclarations
   PyObject *__pyx_t_1 = NULL;
@@ -5286,18 +5704,18 @@ static PyObject *__pyx_pf_9_openjpeg_8__defaults__(CYTHON_UNUSED PyObject *__pyx
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("__defaults__", 1);
   __Pyx_XDECREF(__pyx_r);
-  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 101, __pyx_L1_error)
+  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 113, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
   __Pyx_INCREF(__Pyx_CyFunction_Defaults(__pyx_defaults1, __pyx_self)->__pyx_arg_codec);
   __Pyx_GIVEREF(__Pyx_CyFunction_Defaults(__pyx_defaults1, __pyx_self)->__pyx_arg_codec);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __Pyx_CyFunction_Defaults(__pyx_defaults1, __pyx_self)->__pyx_arg_codec)) __PYX_ERR(0, 101, __pyx_L1_error);
-  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 101, __pyx_L1_error)
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __Pyx_CyFunction_Defaults(__pyx_defaults1, __pyx_self)->__pyx_arg_codec)) __PYX_ERR(0, 113, __pyx_L1_error);
+  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 113, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
   __Pyx_GIVEREF(__pyx_t_1);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1)) __PYX_ERR(0, 101, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1)) __PYX_ERR(0, 113, __pyx_L1_error);
   __Pyx_INCREF(Py_None);
   __Pyx_GIVEREF(Py_None);
-  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, Py_None)) __PYX_ERR(0, 101, __pyx_L1_error);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, Py_None)) __PYX_ERR(0, 113, __pyx_L1_error);
   __pyx_t_1 = 0;
   __pyx_r = __pyx_t_2;
   __pyx_t_2 = 0;
@@ -5374,19 +5792,19 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
           (void)__Pyx_Arg_NewRef_FASTCALL(values[0]);
           kw_args--;
         }
-        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 101, __pyx_L3_error)
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 113, __pyx_L3_error)
         else goto __pyx_L5_argtuple_error;
         CYTHON_FALLTHROUGH;
         case  1:
         if (kw_args > 0) {
           PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_codec);
           if (value) { values[1] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; }
-          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 101, __pyx_L3_error)
+          else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 113, __pyx_L3_error)
         }
       }
       if (unlikely(kw_args > 0)) {
         const Py_ssize_t kwd_pos_args = __pyx_nargs;
-        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "get_parameters") < 0)) __PYX_ERR(0, 101, __pyx_L3_error)
+        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "get_parameters") < 0)) __PYX_ERR(0, 113, __pyx_L3_error)
       }
     } else {
       switch (__pyx_nargs) {
@@ -5402,7 +5820,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
   }
   goto __pyx_L6_skip;
   __pyx_L5_argtuple_error:;
-  __Pyx_RaiseArgtupleInvalid("get_parameters", 0, 1, 2, __pyx_nargs); __PYX_ERR(0, 101, __pyx_L3_error)
+  __Pyx_RaiseArgtupleInvalid("get_parameters", 0, 1, 2, __pyx_nargs); __PYX_ERR(0, 113, __pyx_L3_error)
   __pyx_L6_skip:;
   goto __pyx_L4_argument_unpacking_done;
   __pyx_L3_error:;
@@ -5416,7 +5834,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
   __Pyx_RefNannyFinishContext();
   return NULL;
   __pyx_L4_argument_unpacking_done:;
-  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_codec), (&PyInt_Type), 0, "codec", 1))) __PYX_ERR(0, 101, __pyx_L1_error)
+  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_codec), (&PyInt_Type), 0, "codec", 1))) __PYX_ERR(0, 113, __pyx_L1_error)
   __pyx_r = __pyx_pf_9_openjpeg_4get_parameters(__pyx_self, __pyx_v_fp, __pyx_v_codec);
 
   /* function exit code */
@@ -5458,7 +5876,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
   int __pyx_clineno = 0;
   __Pyx_RefNannySetupContext("get_parameters", 1);
 
-  /* "_openjpeg.pyx":130
+  /* "_openjpeg.pyx":142
  *     """
  *     cdef JPEG2000Parameters param
  *     param.columns = 0             # <<<<<<<<<<<<<<
@@ -5467,7 +5885,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.columns = 0;
 
-  /* "_openjpeg.pyx":131
+  /* "_openjpeg.pyx":143
  *     cdef JPEG2000Parameters param
  *     param.columns = 0
  *     param.rows = 0             # <<<<<<<<<<<<<<
@@ -5476,7 +5894,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.rows = 0;
 
-  /* "_openjpeg.pyx":132
+  /* "_openjpeg.pyx":144
  *     param.columns = 0
  *     param.rows = 0
  *     param.colourspace = 0             # <<<<<<<<<<<<<<
@@ -5485,7 +5903,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.colourspace = 0;
 
-  /* "_openjpeg.pyx":133
+  /* "_openjpeg.pyx":145
  *     param.rows = 0
  *     param.colourspace = 0
  *     param.nr_components = 0             # <<<<<<<<<<<<<<
@@ -5494,7 +5912,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.nr_components = 0;
 
-  /* "_openjpeg.pyx":134
+  /* "_openjpeg.pyx":146
  *     param.colourspace = 0
  *     param.nr_components = 0
  *     param.precision = 0             # <<<<<<<<<<<<<<
@@ -5503,7 +5921,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.precision = 0;
 
-  /* "_openjpeg.pyx":135
+  /* "_openjpeg.pyx":147
  *     param.nr_components = 0
  *     param.precision = 0
  *     param.is_signed = 0             # <<<<<<<<<<<<<<
@@ -5512,7 +5930,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.is_signed = 0;
 
-  /* "_openjpeg.pyx":136
+  /* "_openjpeg.pyx":148
  *     param.precision = 0
  *     param.is_signed = 0
  *     param.nr_tiles = 0             # <<<<<<<<<<<<<<
@@ -5521,7 +5939,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_param.nr_tiles = 0;
 
-  /* "_openjpeg.pyx":139
+  /* "_openjpeg.pyx":151
  * 
  *     # Pointer to the JPEGParameters object
  *     cdef JPEG2000Parameters *p_param = &param             # <<<<<<<<<<<<<<
@@ -5530,7 +5948,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_p_param = (&__pyx_v_param);
 
-  /* "_openjpeg.pyx":142
+  /* "_openjpeg.pyx":154
  * 
  *     # Pointer to J2K data
  *     cdef PyObject* ptr = <PyObject*>fp             # <<<<<<<<<<<<<<
@@ -5539,17 +5957,17 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   __pyx_v_ptr = ((PyObject *)__pyx_v_fp);
 
-  /* "_openjpeg.pyx":145
+  /* "_openjpeg.pyx":157
  * 
  *     # Decode the data - output is written to output_buffer
  *     result = GetParameters(ptr, codec, p_param)             # <<<<<<<<<<<<<<
  *     if result != 0:
  *         try:
  */
-  __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_codec); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 145, __pyx_L1_error)
+  __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_codec); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 157, __pyx_L1_error)
   __pyx_v_result = GetParameters(__pyx_v_ptr, __pyx_t_1, __pyx_v_p_param);
 
-  /* "_openjpeg.pyx":146
+  /* "_openjpeg.pyx":158
  *     # Decode the data - output is written to output_buffer
  *     result = GetParameters(ptr, codec, p_param)
  *     if result != 0:             # <<<<<<<<<<<<<<
@@ -5559,7 +5977,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
   __pyx_t_2 = (__pyx_v_result != 0);
   if (__pyx_t_2) {
 
-    /* "_openjpeg.pyx":147
+    /* "_openjpeg.pyx":159
  *     result = GetParameters(ptr, codec, p_param)
  *     if result != 0:
  *         try:             # <<<<<<<<<<<<<<
@@ -5575,28 +5993,28 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
       __Pyx_XGOTREF(__pyx_t_5);
       /*try:*/ {
 
-        /* "_openjpeg.pyx":148
+        /* "_openjpeg.pyx":160
  *     if result != 0:
  *         try:
  *             msg = f": {ERRORS[result]}"             # <<<<<<<<<<<<<<
  *         except KeyError:
  *             pass
  */
-        __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_n_s_ERRORS); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 148, __pyx_L4_error)
+        __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_n_s_ERRORS); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 160, __pyx_L4_error)
         __Pyx_GOTREF(__pyx_t_6);
-        __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_6, __pyx_v_result, int, 1, __Pyx_PyInt_From_int, 0, 1, 1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 148, __pyx_L4_error)
+        __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_6, __pyx_v_result, int, 1, __Pyx_PyInt_From_int, 0, 1, 1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 160, __pyx_L4_error)
         __Pyx_GOTREF(__pyx_t_7);
         __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
-        __pyx_t_6 = __Pyx_PyObject_FormatSimple(__pyx_t_7, __pyx_empty_unicode); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 148, __pyx_L4_error)
+        __pyx_t_6 = __Pyx_PyObject_FormatSimple(__pyx_t_7, __pyx_empty_unicode); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 160, __pyx_L4_error)
         __Pyx_GOTREF(__pyx_t_6);
         __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        __pyx_t_7 = __Pyx_PyUnicode_Concat(__pyx_kp_u__3, __pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 148, __pyx_L4_error)
+        __pyx_t_7 = __Pyx_PyUnicode_Concat(__pyx_kp_u__3, __pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 160, __pyx_L4_error)
         __Pyx_GOTREF(__pyx_t_7);
         __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
         __pyx_v_msg = ((PyObject*)__pyx_t_7);
         __pyx_t_7 = 0;
 
-        /* "_openjpeg.pyx":147
+        /* "_openjpeg.pyx":159
  *     result = GetParameters(ptr, codec, p_param)
  *     if result != 0:
  *         try:             # <<<<<<<<<<<<<<
@@ -5612,7 +6030,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
       __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
       __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
 
-      /* "_openjpeg.pyx":149
+      /* "_openjpeg.pyx":161
  *         try:
  *             msg = f": {ERRORS[result]}"
  *         except KeyError:             # <<<<<<<<<<<<<<
@@ -5626,7 +6044,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
       }
       goto __pyx_L6_except_error;
 
-      /* "_openjpeg.pyx":147
+      /* "_openjpeg.pyx":159
  *     result = GetParameters(ptr, codec, p_param)
  *     if result != 0:
  *         try:             # <<<<<<<<<<<<<<
@@ -5647,24 +6065,24 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
       __pyx_L9_try_end:;
     }
 
-    /* "_openjpeg.pyx":152
+    /* "_openjpeg.pyx":164
  *             pass
  * 
  *         raise RuntimeError("Error decoding the J2K data" + msg)             # <<<<<<<<<<<<<<
  * 
  *     # From openjpeg.h#L309
  */
-    if (unlikely(!__pyx_v_msg)) { __Pyx_RaiseUnboundLocalError("msg"); __PYX_ERR(0, 152, __pyx_L1_error) }
-    __pyx_t_7 = __Pyx_PyUnicode_Concat(__pyx_kp_u_Error_decoding_the_J2K_data_2, __pyx_v_msg); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 152, __pyx_L1_error)
+    if (unlikely(!__pyx_v_msg)) { __Pyx_RaiseUnboundLocalError("msg"); __PYX_ERR(0, 164, __pyx_L1_error) }
+    __pyx_t_7 = __Pyx_PyUnicode_Concat(__pyx_kp_u_Error_decoding_the_J2K_data, __pyx_v_msg); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 164, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_7);
-    __pyx_t_6 = __Pyx_PyObject_CallOneArg(__pyx_builtin_RuntimeError, __pyx_t_7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 152, __pyx_L1_error)
+    __pyx_t_6 = __Pyx_PyObject_CallOneArg(__pyx_builtin_RuntimeError, __pyx_t_7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 164, __pyx_L1_error)
     __Pyx_GOTREF(__pyx_t_6);
     __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
     __Pyx_Raise(__pyx_t_6, 0, 0, 0);
     __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
-    __PYX_ERR(0, 152, __pyx_L1_error)
+    __PYX_ERR(0, 164, __pyx_L1_error)
 
-    /* "_openjpeg.pyx":146
+    /* "_openjpeg.pyx":158
  *     # Decode the data - output is written to output_buffer
  *     result = GetParameters(ptr, codec, p_param)
  *     if result != 0:             # <<<<<<<<<<<<<<
@@ -5673,26 +6091,26 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
  */
   }
 
-  /* "_openjpeg.pyx":156
+  /* "_openjpeg.pyx":168
  *     # From openjpeg.h#L309
  *     colours = {
  *         -1: "unknown",             # <<<<<<<<<<<<<<
  *          0: "unspecified",
  *          1: "sRGB",
  */
-  __pyx_t_6 = __Pyx_PyDict_NewPresized(7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 156, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyDict_NewPresized(7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 168, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_neg_1, __pyx_n_u_unknown) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_0, __pyx_n_u_unspecified) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_1, __pyx_n_u_sRGB) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_2, __pyx_n_u_monochrome) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_3, __pyx_n_u_YUV) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_4, __pyx_kp_u_e_YCC) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_6, __pyx_int_5, __pyx_n_u_CYMK) < 0) __PYX_ERR(0, 156, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_neg_1, __pyx_n_u_unknown) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_0, __pyx_n_u_unspecified) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_1, __pyx_n_u_sRGB) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_2, __pyx_n_u_monochrome) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_3, __pyx_n_u_YUV) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_4, __pyx_kp_u_e_YCC) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_6, __pyx_int_5, __pyx_n_u_CYMK) < 0) __PYX_ERR(0, 168, __pyx_L1_error)
   __pyx_v_colours = ((PyObject*)__pyx_t_6);
   __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":165
+  /* "_openjpeg.pyx":177
  *     }
  * 
  *     try:             # <<<<<<<<<<<<<<
@@ -5708,22 +6126,22 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
     __Pyx_XGOTREF(__pyx_t_3);
     /*try:*/ {
 
-      /* "_openjpeg.pyx":166
+      /* "_openjpeg.pyx":178
  * 
  *     try:
  *         colourspace = colours[param.colourspace]             # <<<<<<<<<<<<<<
  *     except KeyError:
  *         colourspace = "unknown"
  */
-      __pyx_t_6 = __Pyx_PyInt_From_int(__pyx_v_param.colourspace); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 166, __pyx_L10_error)
+      __pyx_t_6 = __Pyx_PyInt_From_int(__pyx_v_param.colourspace); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 178, __pyx_L10_error)
       __Pyx_GOTREF(__pyx_t_6);
-      __pyx_t_7 = __Pyx_PyDict_GetItem(__pyx_v_colours, __pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 166, __pyx_L10_error)
+      __pyx_t_7 = __Pyx_PyDict_GetItem(__pyx_v_colours, __pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 178, __pyx_L10_error)
       __Pyx_GOTREF(__pyx_t_7);
       __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
       __pyx_v_colourspace = __pyx_t_7;
       __pyx_t_7 = 0;
 
-      /* "_openjpeg.pyx":165
+      /* "_openjpeg.pyx":177
  *     }
  * 
  *     try:             # <<<<<<<<<<<<<<
@@ -5739,7 +6157,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
     __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
     __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
 
-    /* "_openjpeg.pyx":167
+    /* "_openjpeg.pyx":179
  *     try:
  *         colourspace = colours[param.colourspace]
  *     except KeyError:             # <<<<<<<<<<<<<<
@@ -5749,12 +6167,12 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
     __pyx_t_1 = __Pyx_PyErr_ExceptionMatches(__pyx_builtin_KeyError);
     if (__pyx_t_1) {
       __Pyx_AddTraceback("_openjpeg.get_parameters", __pyx_clineno, __pyx_lineno, __pyx_filename);
-      if (__Pyx_GetException(&__pyx_t_7, &__pyx_t_6, &__pyx_t_8) < 0) __PYX_ERR(0, 167, __pyx_L12_except_error)
+      if (__Pyx_GetException(&__pyx_t_7, &__pyx_t_6, &__pyx_t_8) < 0) __PYX_ERR(0, 179, __pyx_L12_except_error)
       __Pyx_XGOTREF(__pyx_t_7);
       __Pyx_XGOTREF(__pyx_t_6);
       __Pyx_XGOTREF(__pyx_t_8);
 
-      /* "_openjpeg.pyx":168
+      /* "_openjpeg.pyx":180
  *         colourspace = colours[param.colourspace]
  *     except KeyError:
  *         colourspace = "unknown"             # <<<<<<<<<<<<<<
@@ -5770,7 +6188,7 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
     }
     goto __pyx_L12_except_error;
 
-    /* "_openjpeg.pyx":165
+    /* "_openjpeg.pyx":177
  *     }
  * 
  *     try:             # <<<<<<<<<<<<<<
@@ -5791,106 +6209,108 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
     __pyx_L15_try_end:;
   }
 
-  /* "_openjpeg.pyx":171
+  /* "_openjpeg.pyx":183
  * 
  *     parameters = {
  *         'rows' : param.rows,             # <<<<<<<<<<<<<<
  *         'columns' : param.columns,
  *         'colourspace' : colourspace,
  */
-  __pyx_t_8 = __Pyx_PyDict_NewPresized(7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 171, __pyx_L1_error)
+  __pyx_t_8 = __Pyx_PyDict_NewPresized(7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_8);
-  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.rows); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 171, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.rows); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_rows, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_rows, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":172
+  /* "_openjpeg.pyx":184
  *     parameters = {
  *         'rows' : param.rows,
  *         'columns' : param.columns,             # <<<<<<<<<<<<<<
  *         'colourspace' : colourspace,
  *         'nr_components' : param.nr_components,
  */
-  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.columns); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 172, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.columns); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 184, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_columns, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_columns, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":173
+  /* "_openjpeg.pyx":185
  *         'rows' : param.rows,
  *         'columns' : param.columns,
  *         'colourspace' : colourspace,             # <<<<<<<<<<<<<<
  *         'nr_components' : param.nr_components,
  *         'precision' : param.precision,
  */
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_colourspace, __pyx_v_colourspace) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_colourspace, __pyx_v_colourspace) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
 
-  /* "_openjpeg.pyx":174
+  /* "_openjpeg.pyx":186
  *         'columns' : param.columns,
  *         'colourspace' : colourspace,
  *         'nr_components' : param.nr_components,             # <<<<<<<<<<<<<<
  *         'precision' : param.precision,
  *         'is_signed' : bool(param.is_signed),
  */
-  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.nr_components); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 174, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.nr_components); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 186, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_nr_components, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_nr_components, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":175
+  /* "_openjpeg.pyx":187
  *         'colourspace' : colourspace,
  *         'nr_components' : param.nr_components,
  *         'precision' : param.precision,             # <<<<<<<<<<<<<<
  *         'is_signed' : bool(param.is_signed),
  *         'nr_tiles' : param.nr_tiles,
  */
-  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.precision); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 175, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.precision); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 187, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_precision, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_precision, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":176
+  /* "_openjpeg.pyx":188
  *         'nr_components' : param.nr_components,
  *         'precision' : param.precision,
  *         'is_signed' : bool(param.is_signed),             # <<<<<<<<<<<<<<
  *         'nr_tiles' : param.nr_tiles,
  *     }
  */
-  __pyx_t_6 = __Pyx_PyInt_From_unsigned_int(__pyx_v_param.is_signed); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 176, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_unsigned_int(__pyx_v_param.is_signed); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 188, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 176, __pyx_L1_error)
+  __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 188, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
-  __pyx_t_6 = __Pyx_PyBool_FromLong((!(!__pyx_t_2))); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 176, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyBool_FromLong((!(!__pyx_t_2))); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 188, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_is_signed, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_is_signed, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 
-  /* "_openjpeg.pyx":177
+  /* "_openjpeg.pyx":189
  *         'precision' : param.precision,
  *         'is_signed' : bool(param.is_signed),
  *         'nr_tiles' : param.nr_tiles,             # <<<<<<<<<<<<<<
  *     }
  * 
  */
-  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.nr_tiles); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 177, __pyx_L1_error)
+  __pyx_t_6 = __Pyx_PyInt_From_uint32_t(__pyx_v_param.nr_tiles); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 189, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_6);
-  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_nr_tiles, __pyx_t_6) < 0) __PYX_ERR(0, 171, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_8, __pyx_n_u_nr_tiles, __pyx_t_6) < 0) __PYX_ERR(0, 183, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
   __pyx_v_parameters = ((PyObject*)__pyx_t_8);
   __pyx_t_8 = 0;
 
-  /* "_openjpeg.pyx":180
+  /* "_openjpeg.pyx":192
  *     }
  * 
  *     return parameters             # <<<<<<<<<<<<<<
+ * 
+ * 
  */
   __Pyx_XDECREF(__pyx_r);
   __Pyx_INCREF(__pyx_v_parameters);
   __pyx_r = __pyx_v_parameters;
   goto __pyx_L0;
 
-  /* "_openjpeg.pyx":101
+  /* "_openjpeg.pyx":113
  * 
  * 
  * def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bool]]:             # <<<<<<<<<<<<<<
@@ -5915,72 +6335,1436 @@ static PyObject *__pyx_pf_9_openjpeg_4get_parameters(CYTHON_UNUSED PyObject *__p
   return __pyx_r;
 }
 
-static PyMethodDef __pyx_methods[] = {
-  {0, 0, 0, 0}
-};
-#ifndef CYTHON_SMALL_CODE
-#if defined(__clang__)
-    #define CYTHON_SMALL_CODE
-#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
-    #define CYTHON_SMALL_CODE __attribute__((cold))
+/* "_openjpeg.pyx":195
+ * 
+ * 
+ * def encode(             # <<<<<<<<<<<<<<
+ *     cnp.ndarray arr,
+ *     int bits_stored,
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9_openjpeg_7encode(PyObject *__pyx_self, 
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
 #else
-    #define CYTHON_SMALL_CODE
+PyObject *__pyx_args, PyObject *__pyx_kwds
 #endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9_openjpeg_6encode, "Return the JPEG 2000 compressed `arr`.\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The array containing the image data to be encoded.\n    bits_stored : int, optional\n        The number of bits used per pixel.\n    photometric_interpretation : int, optional\n        The colour space of the unencoded image data that will be set in the\n        JPEG 2000 metadata.\n    use_mct : bool, optional\n        If ``True`` then apply multi-component transformation (MCT) to RGB\n        images.\n    lossless : bool, optional\n        If ``True`` then use lossless encoding, otherwise use lossy encoding.\n    compression_ratios : list[float], optional\n        Required if using lossy encoding, this is the compression ratio to use\n        for each layer. Should be in decreasing order (such as ``[80, 30, 10]``)\n        and the final value may be ``1`` to indicate lossless encoding should\n        be used for that layer.\n    codec_format : int, optional\n        The codec to used when encoding:\n\n        * ``0``: JPEG 2000 codestream only (default) (J2K/J2C format)\n        * ``2``: A boxed JPEG 2000 codestream (JP2 format)\n\n    Returns\n    -------\n    int\n        The return code of the encoding, will be ``0`` for success, otherwise\n        encoding failed.\n    ");
+static PyMethodDef __pyx_mdef_9_openjpeg_7encode = {"encode", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9_openjpeg_7encode, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9_openjpeg_6encode};
+static PyObject *__pyx_pw_9_openjpeg_7encode(PyObject *__pyx_self, 
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
 #endif
-/* #### Code section: pystring_table ### */
-
-static int __Pyx_CreateStringTabAndInitStrings(void) {
-  __Pyx_StringTabEntry __pyx_string_tab[] = {
-    {&__pyx_n_s_BinaryIO, __pyx_k_BinaryIO, sizeof(__pyx_k_BinaryIO), 0, 0, 1, 1},
-    {&__pyx_n_u_CYMK, __pyx_k_CYMK, sizeof(__pyx_k_CYMK), 0, 1, 0, 1},
-    {&__pyx_n_s_Dict, __pyx_k_Dict, sizeof(__pyx_k_Dict), 0, 0, 1, 1},
-    {&__pyx_kp_s_Dict_str_Union_str_int_bool, __pyx_k_Dict_str_Union_str_int_bool, sizeof(__pyx_k_Dict_str_Union_str_int_bool), 0, 0, 1, 0},
-    {&__pyx_n_s_ERRORS, __pyx_k_ERRORS, sizeof(__pyx_k_ERRORS), 0, 0, 1, 1},
-    {&__pyx_kp_u_Error_decoding_the_J2K_data, __pyx_k_Error_decoding_the_J2K_data, sizeof(__pyx_k_Error_decoding_the_J2K_data), 0, 1, 0, 0},
-    {&__pyx_kp_u_Error_decoding_the_J2K_data_2, __pyx_k_Error_decoding_the_J2K_data_2, sizeof(__pyx_k_Error_decoding_the_J2K_data_2), 0, 1, 0, 0},
-    {&__pyx_n_s_ImportError, __pyx_k_ImportError, sizeof(__pyx_k_ImportError), 0, 0, 1, 1},
-    {&__pyx_n_s_KeyError, __pyx_k_KeyError, sizeof(__pyx_k_KeyError), 0, 0, 1, 1},
-    {&__pyx_n_s_RuntimeError, __pyx_k_RuntimeError, sizeof(__pyx_k_RuntimeError), 0, 0, 1, 1},
-    {&__pyx_n_s_Union, __pyx_k_Union, sizeof(__pyx_k_Union), 0, 0, 1, 1},
-    {&__pyx_kp_s_Union_np_ndarray_bytearray, __pyx_k_Union_np_ndarray_bytearray, sizeof(__pyx_k_Union_np_ndarray_bytearray), 0, 0, 1, 0},
-    {&__pyx_n_u_YUV, __pyx_k_YUV, sizeof(__pyx_k_YUV), 0, 1, 0, 1},
-    {&__pyx_n_s__12, __pyx_k__12, sizeof(__pyx_k__12), 0, 0, 1, 1},
-    {&__pyx_kp_u__3, __pyx_k__3, sizeof(__pyx_k__3), 0, 1, 0, 0},
-    {&__pyx_kp_u__4, __pyx_k__4, sizeof(__pyx_k__4), 0, 1, 0, 0},
-    {&__pyx_n_s__5, __pyx_k__5, sizeof(__pyx_k__5), 0, 0, 1, 1},
-    {&__pyx_n_s_as_array, __pyx_k_as_array, sizeof(__pyx_k_as_array), 0, 0, 1, 1},
-    {&__pyx_n_s_asyncio_coroutines, __pyx_k_asyncio_coroutines, sizeof(__pyx_k_asyncio_coroutines), 0, 0, 1, 1},
-    {&__pyx_n_s_bool, __pyx_k_bool, sizeof(__pyx_k_bool), 0, 0, 1, 1},
-    {&__pyx_n_s_bpp, __pyx_k_bpp, sizeof(__pyx_k_bpp), 0, 0, 1, 1},
-    {&__pyx_n_s_bytes, __pyx_k_bytes, sizeof(__pyx_k_bytes), 0, 0, 1, 1},
-    {&__pyx_n_s_ceil, __pyx_k_ceil, sizeof(__pyx_k_ceil), 0, 0, 1, 1},
-    {&__pyx_n_s_cline_in_traceback, __pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 0, 1, 1},
-    {&__pyx_n_s_codec, __pyx_k_codec, sizeof(__pyx_k_codec), 0, 0, 1, 1},
-    {&__pyx_n_s_colours, __pyx_k_colours, sizeof(__pyx_k_colours), 0, 0, 1, 1},
-    {&__pyx_n_s_colourspace, __pyx_k_colourspace, sizeof(__pyx_k_colourspace), 0, 0, 1, 1},
-    {&__pyx_n_u_colourspace, __pyx_k_colourspace, sizeof(__pyx_k_colourspace), 0, 1, 0, 1},
-    {&__pyx_n_u_columns, __pyx_k_columns, sizeof(__pyx_k_columns), 0, 1, 0, 1},
-    {&__pyx_n_s_decode, __pyx_k_decode, sizeof(__pyx_k_decode), 0, 0, 1, 1},
-    {&__pyx_n_s_dtype, __pyx_k_dtype, sizeof(__pyx_k_dtype), 0, 0, 1, 1},
-    {&__pyx_kp_u_e_YCC, __pyx_k_e_YCC, sizeof(__pyx_k_e_YCC), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_create_the_input_strea, __pyx_k_failed_to_create_the_input_strea, sizeof(__pyx_k_failed_to_create_the_input_strea), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_decode_image, __pyx_k_failed_to_decode_image, sizeof(__pyx_k_failed_to_decode_image), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_read_the_header, __pyx_k_failed_to_read_the_header, sizeof(__pyx_k_failed_to_read_the_header), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_set_the_component_indi, __pyx_k_failed_to_set_the_component_indi, sizeof(__pyx_k_failed_to_set_the_component_indi), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_set_the_decoded_area, __pyx_k_failed_to_set_the_decoded_area, sizeof(__pyx_k_failed_to_set_the_decoded_area), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_setup_the_decoder, __pyx_k_failed_to_setup_the_decoder, sizeof(__pyx_k_failed_to_setup_the_decoder), 0, 1, 0, 0},
-    {&__pyx_kp_u_failed_to_upscale_subsampled_com, __pyx_k_failed_to_upscale_subsampled_com, sizeof(__pyx_k_failed_to_upscale_subsampled_com), 0, 1, 0, 0},
-    {&__pyx_n_s_fp, __pyx_k_fp, sizeof(__pyx_k_fp), 0, 0, 1, 1},
-    {&__pyx_n_s_get, __pyx_k_get, sizeof(__pyx_k_get), 0, 0, 1, 1},
-    {&__pyx_n_s_get_parameters, __pyx_k_get_parameters, sizeof(__pyx_k_get_parameters), 0, 0, 1, 1},
-    {&__pyx_n_s_get_version, __pyx_k_get_version, sizeof(__pyx_k_get_version), 0, 0, 1, 1},
-    {&__pyx_n_s_import, __pyx_k_import, sizeof(__pyx_k_import), 0, 0, 1, 1},
-    {&__pyx_n_s_initializing, __pyx_k_initializing, sizeof(__pyx_k_initializing), 0, 0, 1, 1},
-    {&__pyx_n_s_int, __pyx_k_int, sizeof(__pyx_k_int), 0, 0, 1, 1},
-    {&__pyx_n_s_is_coroutine, __pyx_k_is_coroutine, sizeof(__pyx_k_is_coroutine), 0, 0, 1, 1},
+) {
+  PyArrayObject *__pyx_v_arr = 0;
+  int __pyx_v_bits_stored;
+  int __pyx_v_photometric_interpretation;
+  int __pyx_v_use_mct;
+  PyObject *__pyx_v_compression_ratios = 0;
+  PyObject *__pyx_v_signal_noise_ratios = 0;
+  int __pyx_v_codec_format;
+  #if !CYTHON_METH_FASTCALL
+  CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+  #endif
+  CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+  PyObject* values[7] = {0,0,0,0,0,0,0};
+  int __pyx_lineno = 0;
+  const char *__pyx_filename = NULL;
+  int __pyx_clineno = 0;
+  PyObject *__pyx_r = 0;
+  __Pyx_RefNannyDeclarations
+  __Pyx_RefNannySetupContext("encode (wrapper)", 0);
+  #if !CYTHON_METH_FASTCALL
+  #if CYTHON_ASSUME_SAFE_MACROS
+  __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+  #else
+  __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+  #endif
+  #endif
+  __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+  {
+    PyObject **__pyx_pyargnames[] = {&__pyx_n_s_arr,&__pyx_n_s_bits_stored,&__pyx_n_s_photometric_interpretation,&__pyx_n_s_use_mct,&__pyx_n_s_compression_ratios,&__pyx_n_s_signal_noise_ratios,&__pyx_n_s_codec_format,0};
+    if (__pyx_kwds) {
+      Py_ssize_t kw_args;
+      switch (__pyx_nargs) {
+        case  7: values[6] = __Pyx_Arg_FASTCALL(__pyx_args, 6);
+        CYTHON_FALLTHROUGH;
+        case  6: values[5] = __Pyx_Arg_FASTCALL(__pyx_args, 5);
+        CYTHON_FALLTHROUGH;
+        case  5: values[4] = __Pyx_Arg_FASTCALL(__pyx_args, 4);
+        CYTHON_FALLTHROUGH;
+        case  4: values[3] = __Pyx_Arg_FASTCALL(__pyx_args, 3);
+        CYTHON_FALLTHROUGH;
+        case  3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2);
+        CYTHON_FALLTHROUGH;
+        case  2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1);
+        CYTHON_FALLTHROUGH;
+        case  1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0);
+        CYTHON_FALLTHROUGH;
+        case  0: break;
+        default: goto __pyx_L5_argtuple_error;
+      }
+      kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds);
+      switch (__pyx_nargs) {
+        case  0:
+        if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_arr)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[0]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else goto __pyx_L5_argtuple_error;
+        CYTHON_FALLTHROUGH;
+        case  1:
+        if (likely((values[1] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_bits_stored)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[1]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 1); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+        CYTHON_FALLTHROUGH;
+        case  2:
+        if (likely((values[2] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_photometric_interpretation)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[2]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 2); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+        CYTHON_FALLTHROUGH;
+        case  3:
+        if (likely((values[3] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_use_mct)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[3]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 3); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+        CYTHON_FALLTHROUGH;
+        case  4:
+        if (likely((values[4] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_compression_ratios)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[4]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 4); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+        CYTHON_FALLTHROUGH;
+        case  5:
+        if (likely((values[5] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_signal_noise_ratios)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[5]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 5); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+        CYTHON_FALLTHROUGH;
+        case  6:
+        if (likely((values[6] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_codec_format)) != 0)) {
+          (void)__Pyx_Arg_NewRef_FASTCALL(values[6]);
+          kw_args--;
+        }
+        else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 195, __pyx_L3_error)
+        else {
+          __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, 6); __PYX_ERR(0, 195, __pyx_L3_error)
+        }
+      }
+      if (unlikely(kw_args > 0)) {
+        const Py_ssize_t kwd_pos_args = __pyx_nargs;
+        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "encode") < 0)) __PYX_ERR(0, 195, __pyx_L3_error)
+      }
+    } else if (unlikely(__pyx_nargs != 7)) {
+      goto __pyx_L5_argtuple_error;
+    } else {
+      values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0);
+      values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1);
+      values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2);
+      values[3] = __Pyx_Arg_FASTCALL(__pyx_args, 3);
+      values[4] = __Pyx_Arg_FASTCALL(__pyx_args, 4);
+      values[5] = __Pyx_Arg_FASTCALL(__pyx_args, 5);
+      values[6] = __Pyx_Arg_FASTCALL(__pyx_args, 6);
+    }
+    __pyx_v_arr = ((PyArrayObject *)values[0]);
+    __pyx_v_bits_stored = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_bits_stored == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 197, __pyx_L3_error)
+    __pyx_v_photometric_interpretation = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_photometric_interpretation == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 198, __pyx_L3_error)
+    __pyx_v_use_mct = __Pyx_PyObject_IsTrue(values[3]); if (unlikely((__pyx_v_use_mct == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 199, __pyx_L3_error)
+    __pyx_v_compression_ratios = ((PyObject*)values[4]);
+    __pyx_v_signal_noise_ratios = ((PyObject*)values[5]);
+    __pyx_v_codec_format = __Pyx_PyInt_As_int(values[6]); if (unlikely((__pyx_v_codec_format == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 202, __pyx_L3_error)
+  }
+  goto __pyx_L6_skip;
+  __pyx_L5_argtuple_error:;
+  __Pyx_RaiseArgtupleInvalid("encode", 1, 7, 7, __pyx_nargs); __PYX_ERR(0, 195, __pyx_L3_error)
+  __pyx_L6_skip:;
+  goto __pyx_L4_argument_unpacking_done;
+  __pyx_L3_error:;
+  {
+    Py_ssize_t __pyx_temp;
+    for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+      __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]);
+    }
+  }
+  __Pyx_AddTraceback("_openjpeg.encode", __pyx_clineno, __pyx_lineno, __pyx_filename);
+  __Pyx_RefNannyFinishContext();
+  return NULL;
+  __pyx_L4_argument_unpacking_done:;
+  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_arr), __pyx_ptype_5numpy_ndarray, 1, "arr", 0))) __PYX_ERR(0, 196, __pyx_L1_error)
+  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_compression_ratios), (&PyList_Type), 1, "compression_ratios", 1))) __PYX_ERR(0, 200, __pyx_L1_error)
+  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_signal_noise_ratios), (&PyList_Type), 1, "signal_noise_ratios", 1))) __PYX_ERR(0, 201, __pyx_L1_error)
+  __pyx_r = __pyx_pf_9_openjpeg_6encode(__pyx_self, __pyx_v_arr, __pyx_v_bits_stored, __pyx_v_photometric_interpretation, __pyx_v_use_mct, __pyx_v_compression_ratios, __pyx_v_signal_noise_ratios, __pyx_v_codec_format);
+
+  /* function exit code */
+  goto __pyx_L0;
+  __pyx_L1_error:;
+  __pyx_r = NULL;
+  __pyx_L0:;
+  {
+    Py_ssize_t __pyx_temp;
+    for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+      __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]);
+    }
+  }
+  __Pyx_RefNannyFinishContext();
+  return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9_openjpeg_6encode(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_arr, int __pyx_v_bits_stored, int __pyx_v_photometric_interpretation, int __pyx_v_use_mct, PyObject *__pyx_v_compression_ratios, PyObject *__pyx_v_signal_noise_ratios, int __pyx_v_codec_format) {
+  PyObject *__pyx_v_allowed = NULL;
+  PyObject *__pyx_v_arr_max = NULL;
+  PyObject *__pyx_v_arr_min = NULL;
+  PyObject *__pyx_v_dst = NULL;
+  int __pyx_v_return_code;
+  PyObject *__pyx_7genexpr__pyx_v_x = NULL;
+  PyObject *__pyx_8genexpr1__pyx_v_x = NULL;
+  PyObject *__pyx_r = NULL;
+  __Pyx_RefNannyDeclarations
+  PyObject *__pyx_t_1 = NULL;
+  PyObject *__pyx_t_2 = NULL;
+  PyObject *__pyx_t_3 = NULL;
+  PyObject *__pyx_t_4 = NULL;
+  int __pyx_t_5;
+  int __pyx_t_6;
+  Py_ssize_t __pyx_t_7;
+  Py_UCS4 __pyx_t_8;
+  PyObject *__pyx_t_9 = NULL;
+  PyObject *__pyx_t_10 = NULL;
+  PyObject *__pyx_t_11 = NULL;
+  int __pyx_t_12;
+  int __pyx_t_13;
+  int __pyx_lineno = 0;
+  const char *__pyx_filename = NULL;
+  int __pyx_clineno = 0;
+  __Pyx_RefNannySetupContext("encode", 0);
+  __Pyx_INCREF(__pyx_v_compression_ratios);
+  __Pyx_INCREF(__pyx_v_signal_noise_ratios);
+
+  /* "_openjpeg.pyx":237
+ *         encoding failed.
+ *     """
+ *     if not (1 <= bits_stored <= arr.dtype.itemsize * 8):             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "
+ */
+  __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_bits_stored); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 237, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  __pyx_t_2 = PyObject_RichCompare(__pyx_int_1, __pyx_t_1, Py_LE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 237, __pyx_L1_error)
+  if (__Pyx_PyObject_IsTrue(__pyx_t_2)) {
+    __Pyx_DECREF(__pyx_t_2);
+    __pyx_t_3 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 237, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_3);
+    __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_itemsize); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 237, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_4);
+    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+    __pyx_t_3 = __Pyx_PyInt_MultiplyObjC(__pyx_t_4, __pyx_int_8, 8, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 237, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_3);
+    __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+    __pyx_t_2 = PyObject_RichCompare(__pyx_t_1, __pyx_t_3, Py_LE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 237, __pyx_L1_error)
+    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  }
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 237, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_6 = (!__pyx_t_5);
+  if (unlikely(__pyx_t_6)) {
+
+    /* "_openjpeg.pyx":239
+ *     if not (1 <= bits_stored <= arr.dtype.itemsize * 8):
+ *         raise ValueError(
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "             # <<<<<<<<<<<<<<
+ *             f"in the range (1, {arr.dtype.itemsize * 8})"
+ *         )
+ */
+    __pyx_t_2 = PyTuple_New(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 239, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __pyx_t_7 = 0;
+    __pyx_t_8 = 127;
+    __Pyx_INCREF(__pyx_kp_u_Invalid_value_for_the_bits_store);
+    __pyx_t_7 += 82;
+    __Pyx_GIVEREF(__pyx_kp_u_Invalid_value_for_the_bits_store);
+    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_kp_u_Invalid_value_for_the_bits_store);
+
+    /* "_openjpeg.pyx":240
+ *         raise ValueError(
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "
+ *             f"in the range (1, {arr.dtype.itemsize * 8})"             # <<<<<<<<<<<<<<
+ *         )
+ * 
+ */
+    __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 240, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_1);
+    __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_itemsize); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 240, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_3);
+    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+    __pyx_t_1 = __Pyx_PyInt_MultiplyObjC(__pyx_t_3, __pyx_int_8, 8, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 240, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_1);
+    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+    __pyx_t_3 = __Pyx_PyObject_FormatSimple(__pyx_t_1, __pyx_empty_unicode); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 240, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_3);
+    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_3);
+    __Pyx_GIVEREF(__pyx_t_3);
+    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3);
+    __pyx_t_3 = 0;
+    __Pyx_INCREF(__pyx_kp_u__4);
+    __pyx_t_7 += 1;
+    __Pyx_GIVEREF(__pyx_kp_u__4);
+    PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u__4);
+
+    /* "_openjpeg.pyx":239
+ *     if not (1 <= bits_stored <= arr.dtype.itemsize * 8):
+ *         raise ValueError(
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "             # <<<<<<<<<<<<<<
+ *             f"in the range (1, {arr.dtype.itemsize * 8})"
+ *         )
+ */
+    __pyx_t_3 = __Pyx_PyUnicode_Join(__pyx_t_2, 3, __pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 239, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_3);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+    /* "_openjpeg.pyx":238
+ *     """
+ *     if not (1 <= bits_stored <= arr.dtype.itemsize * 8):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "
+ *             f"in the range (1, {arr.dtype.itemsize * 8})"
+ */
+    __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_ValueError, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 238, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 238, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":237
+ *         encoding failed.
+ *     """
+ *     if not (1 <= bits_stored <= arr.dtype.itemsize * 8):             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "Invalid value for the 'bits_stored' parameter, the value must be "
+ */
+  }
+
+  /* "_openjpeg.pyx":243
+ *         )
+ * 
+ *     allowed = (bool, np.uint8, np.int8, np.uint16, np.int16, np.uint32, np.int32)             # <<<<<<<<<<<<<<
+ *     if arr.dtype not in allowed:
+ *         raise ValueError(
+ */
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint8); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_int8); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint16); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_9 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_int16); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_9);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint32); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_11 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_int32); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = PyTuple_New(7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 243, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_INCREF(((PyObject*)&PyBool_Type));
+  __Pyx_GIVEREF(((PyObject*)&PyBool_Type));
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject*)&PyBool_Type))) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_3);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_1);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_t_1)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_4);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 3, __pyx_t_4)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_9);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 4, __pyx_t_9)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_10);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 5, __pyx_t_10)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_11);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 6, __pyx_t_11)) __PYX_ERR(0, 243, __pyx_L1_error);
+  __pyx_t_3 = 0;
+  __pyx_t_1 = 0;
+  __pyx_t_4 = 0;
+  __pyx_t_9 = 0;
+  __pyx_t_10 = 0;
+  __pyx_t_11 = 0;
+  __pyx_v_allowed = ((PyObject*)__pyx_t_2);
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":244
+ * 
+ *     allowed = (bool, np.uint8, np.int8, np.uint16, np.int16, np.uint32, np.int32)
+ *     if arr.dtype not in allowed:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             f"The input array has an unsupported dtype '{arr.dtype}', only bool, "
+ */
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 244, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_6 = (__Pyx_PySequence_ContainsTF(__pyx_t_2, __pyx_v_allowed, Py_NE)); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 244, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  if (unlikely(__pyx_t_6)) {
+
+    /* "_openjpeg.pyx":246
+ *     if arr.dtype not in allowed:
+ *         raise ValueError(
+ *             f"The input array has an unsupported dtype '{arr.dtype}', only bool, "             # <<<<<<<<<<<<<<
+ *             "u1, u2, u4, i1, i2 and i4 are supported"
+ *         )
+ */
+    __pyx_t_2 = PyTuple_New(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 246, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __pyx_t_7 = 0;
+    __pyx_t_8 = 127;
+    __Pyx_INCREF(__pyx_kp_u_The_input_array_has_an_unsupport);
+    __pyx_t_7 += 42;
+    __Pyx_GIVEREF(__pyx_kp_u_The_input_array_has_an_unsupport);
+    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_kp_u_The_input_array_has_an_unsupport);
+    __pyx_t_11 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 246, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_10 = __Pyx_PyObject_FormatSimple(__pyx_t_11, __pyx_empty_unicode); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 246, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_10);
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_10) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_10) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_10);
+    __Pyx_GIVEREF(__pyx_t_10);
+    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_10);
+    __pyx_t_10 = 0;
+    __Pyx_INCREF(__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4);
+    __pyx_t_7 += 53;
+    __Pyx_GIVEREF(__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4);
+    PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4);
+    __pyx_t_10 = __Pyx_PyUnicode_Join(__pyx_t_2, 3, __pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 246, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_10);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+    /* "_openjpeg.pyx":245
+ *     allowed = (bool, np.uint8, np.int8, np.uint16, np.int16, np.uint32, np.int32)
+ *     if arr.dtype not in allowed:
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             f"The input array has an unsupported dtype '{arr.dtype}', only bool, "
+ *             "u1, u2, u4, i1, i2 and i4 are supported"
+ */
+    __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_ValueError, __pyx_t_10); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 245, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 245, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":244
+ * 
+ *     allowed = (bool, np.uint8, np.int8, np.uint16, np.int16, np.uint32, np.int32)
+ *     if arr.dtype not in allowed:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             f"The input array has an unsupported dtype '{arr.dtype}', only bool, "
+ */
+  }
+
+  /* "_openjpeg.pyx":253
+ *     #   based on their use of OPJ_INT32 for pixel values, it should be 32-bit for
+ *     #   signed and 31 bit for unsigned. Maybe I've made a mistake somewhere?
+ *     arr_max = arr.max()             # <<<<<<<<<<<<<<
+ *     arr_min = arr.min()
+ *     if (
+ */
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_max); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 253, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __pyx_t_11 = NULL;
+  __pyx_t_12 = 0;
+  #if CYTHON_UNPACK_METHODS
+  if (likely(PyMethod_Check(__pyx_t_10))) {
+    __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_10);
+    if (likely(__pyx_t_11)) {
+      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_10);
+      __Pyx_INCREF(__pyx_t_11);
+      __Pyx_INCREF(function);
+      __Pyx_DECREF_SET(__pyx_t_10, function);
+      __pyx_t_12 = 1;
+    }
+  }
+  #endif
+  {
+    PyObject *__pyx_callargs[2] = {__pyx_t_11, NULL};
+    __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_10, __pyx_callargs+1-__pyx_t_12, 0+__pyx_t_12);
+    __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+    if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 253, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  }
+  __pyx_v_arr_max = __pyx_t_2;
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":254
+ *     #   signed and 31 bit for unsigned. Maybe I've made a mistake somewhere?
+ *     arr_max = arr.max()
+ *     arr_min = arr.min()             # <<<<<<<<<<<<<<
+ *     if (
+ *         (arr.dtype == np.uint32 and arr_max > 2**24 - 1)
+ */
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_min); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 254, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __pyx_t_11 = NULL;
+  __pyx_t_12 = 0;
+  #if CYTHON_UNPACK_METHODS
+  if (likely(PyMethod_Check(__pyx_t_10))) {
+    __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_10);
+    if (likely(__pyx_t_11)) {
+      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_10);
+      __Pyx_INCREF(__pyx_t_11);
+      __Pyx_INCREF(function);
+      __Pyx_DECREF_SET(__pyx_t_10, function);
+      __pyx_t_12 = 1;
+    }
+  }
+  #endif
+  {
+    PyObject *__pyx_callargs[2] = {__pyx_t_11, NULL};
+    __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_10, __pyx_callargs+1-__pyx_t_12, 0+__pyx_t_12);
+    __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+    if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 254, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  }
+  __pyx_v_arr_min = __pyx_t_2;
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":256
+ *     arr_min = arr.min()
+ *     if (
+ *         (arr.dtype == np.uint32 and arr_max > 2**24 - 1)             # <<<<<<<<<<<<<<
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+ *     ):
+ */
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_GetModuleGlobalName(__pyx_t_10, __pyx_n_s_np); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __pyx_t_11 = __Pyx_PyObject_GetAttrStr(__pyx_t_10, __pyx_n_s_uint32); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_10 = PyObject_RichCompare(__pyx_t_2, __pyx_t_11, Py_EQ); __Pyx_XGOTREF(__pyx_t_10); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_10); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  if (!__pyx_t_5) {
+    goto __pyx_L7_next_or;
+  } else {
+  }
+  __pyx_t_10 = PyObject_RichCompare(__pyx_v_arr_max, __pyx_int_16777215, Py_GT); __Pyx_XGOTREF(__pyx_t_10); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 256, __pyx_L1_error)
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_10); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 256, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  if (!__pyx_t_5) {
+  } else {
+    __pyx_t_6 = __pyx_t_5;
+    goto __pyx_L6_bool_binop_done;
+  }
+  __pyx_L7_next_or:;
+
+  /* "_openjpeg.pyx":257
+ *     if (
+ *         (arr.dtype == np.uint32 and arr_max > 2**24 - 1)
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))             # <<<<<<<<<<<<<<
+ *     ):
+ *         raise ValueError(
+ */
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_np); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_11, __pyx_n_s_int32); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_11 = PyObject_RichCompare(__pyx_t_10, __pyx_t_2, Py_EQ); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  if (__pyx_t_5) {
+  } else {
+    __pyx_t_6 = __pyx_t_5;
+    goto __pyx_L6_bool_binop_done;
+  }
+  __pyx_t_11 = PyObject_RichCompare(__pyx_v_arr_max, __pyx_int_8388607, Py_GT); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  if (!__pyx_t_5) {
+  } else {
+    __pyx_t_6 = __pyx_t_5;
+    goto __pyx_L6_bool_binop_done;
+  }
+  __pyx_t_11 = PyObject_RichCompare(__pyx_v_arr_min, __pyx_int_neg_8388608, Py_LT); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 257, __pyx_L1_error)
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 257, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_6 = __pyx_t_5;
+  __pyx_L6_bool_binop_done:;
+
+  /* "_openjpeg.pyx":255
+ *     arr_max = arr.max()
+ *     arr_min = arr.min()
+ *     if (             # <<<<<<<<<<<<<<
+ *         (arr.dtype == np.uint32 and arr_max > 2**24 - 1)
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+ */
+  if (unlikely(__pyx_t_6)) {
+
+    /* "_openjpeg.pyx":259
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+ *     ):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "The input array contains values outside the range of the maximum "
+ *             "supported bit-depth of 24"
+ */
+    __pyx_t_11 = __Pyx_PyObject_Call(__pyx_builtin_ValueError, __pyx_tuple__5, NULL); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 259, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __Pyx_Raise(__pyx_t_11, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __PYX_ERR(0, 259, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":255
+ *     arr_max = arr.max()
+ *     arr_min = arr.min()
+ *     if (             # <<<<<<<<<<<<<<
+ *         (arr.dtype == np.uint32 and arr_max > 2**24 - 1)
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+ */
+  }
+
+  /* "_openjpeg.pyx":265
+ * 
+ *     # Check the array matches bits_stored
+ *     if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ */
+  __pyx_t_11 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = PyObject_RichCompare(__pyx_t_11, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  if (!__pyx_t_13) {
+  } else {
+    __pyx_t_5 = __pyx_t_13;
+    goto __pyx_L14_bool_binop_done;
+  }
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint16); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = PyObject_RichCompare(__pyx_t_11, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  if (!__pyx_t_13) {
+  } else {
+    __pyx_t_5 = __pyx_t_13;
+    goto __pyx_L14_bool_binop_done;
+  }
+  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_uint32); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = PyObject_RichCompare(__pyx_t_11, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_5 = __pyx_t_13;
+  __pyx_L14_bool_binop_done:;
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_13 = __pyx_t_5;
+  if (__pyx_t_13) {
+  } else {
+    __pyx_t_6 = __pyx_t_13;
+    goto __pyx_L12_bool_binop_done;
+  }
+  __pyx_t_11 = PyFloat_FromDouble((pow(2.0, ((double)__pyx_v_bits_stored)) - 1.0)); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_2 = PyObject_RichCompare(__pyx_v_arr_max, __pyx_t_11, Py_GT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 265, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_6 = __pyx_t_13;
+  __pyx_L12_bool_binop_done:;
+  if (unlikely(__pyx_t_6)) {
+
+    /* "_openjpeg.pyx":267
+ *     if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "             # <<<<<<<<<<<<<<
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ *         )
+ */
+    __pyx_t_2 = PyTuple_New(7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 267, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __pyx_t_7 = 0;
+    __pyx_t_8 = 127;
+    __Pyx_INCREF(__pyx_kp_u_A_bits_stored_value_of);
+    __pyx_t_7 += 25;
+    __Pyx_GIVEREF(__pyx_kp_u_A_bits_stored_value_of);
+    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_kp_u_A_bits_stored_value_of);
+    __pyx_t_11 = __Pyx_PyUnicode_From_int(__pyx_v_bits_stored, 0, ' ', 'd'); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 267, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u_is_incompatible_with_the_range);
+    __pyx_t_7 += 67;
+    __Pyx_GIVEREF(__pyx_kp_u_is_incompatible_with_the_range);
+    PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u_is_incompatible_with_the_range);
+
+    /* "_openjpeg.pyx":268
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"             # <<<<<<<<<<<<<<
+ *         )
+ * 
+ */
+    __pyx_t_11 = __Pyx_PyObject_FormatSimple(__pyx_v_arr_min, __pyx_empty_unicode); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 268, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 3, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u__6);
+    __pyx_t_7 += 2;
+    __Pyx_GIVEREF(__pyx_kp_u__6);
+    PyTuple_SET_ITEM(__pyx_t_2, 4, __pyx_kp_u__6);
+    __pyx_t_11 = __Pyx_PyObject_FormatSimple(__pyx_v_arr_max, __pyx_empty_unicode); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 268, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 5, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u__4);
+    __pyx_t_7 += 1;
+    __Pyx_GIVEREF(__pyx_kp_u__4);
+    PyTuple_SET_ITEM(__pyx_t_2, 6, __pyx_kp_u__4);
+
+    /* "_openjpeg.pyx":267
+ *     if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "             # <<<<<<<<<<<<<<
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ *         )
+ */
+    __pyx_t_11 = __Pyx_PyUnicode_Join(__pyx_t_2, 7, __pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 267, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+    /* "_openjpeg.pyx":266
+ *     # Check the array matches bits_stored
+ *     if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ */
+    __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_ValueError, __pyx_t_11); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 266, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 266, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":265
+ * 
+ *     # Check the array matches bits_stored
+ *     if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ */
+  }
+
+  /* "_openjpeg.pyx":272
+ * 
+ *     if (
+ *         arr.dtype in (np.int8, np.int16, np.int32)             # <<<<<<<<<<<<<<
+ *         and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))
+ *     ):
+ */
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_arr), __pyx_n_s_dtype); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_np); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_11, __pyx_n_s_int8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_11 = PyObject_RichCompare(__pyx_t_2, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  if (!__pyx_t_5) {
+  } else {
+    __pyx_t_13 = __pyx_t_5;
+    goto __pyx_L20_bool_binop_done;
+  }
+  __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_np); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_11, __pyx_n_s_int16); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_11 = PyObject_RichCompare(__pyx_t_2, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  if (!__pyx_t_5) {
+  } else {
+    __pyx_t_13 = __pyx_t_5;
+    goto __pyx_L20_bool_binop_done;
+  }
+  __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_np); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_t_11, __pyx_n_s_int32); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_11 = PyObject_RichCompare(__pyx_t_2, __pyx_t_10, Py_EQ); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 272, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_13 = __pyx_t_5;
+  __pyx_L20_bool_binop_done:;
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_5 = __pyx_t_13;
+  if (__pyx_t_5) {
+  } else {
+    __pyx_t_6 = __pyx_t_5;
+    goto __pyx_L18_bool_binop_done;
+  }
+
+  /* "_openjpeg.pyx":273
+ *     if (
+ *         arr.dtype in (np.int8, np.int16, np.int32)
+ *         and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))             # <<<<<<<<<<<<<<
+ *     ):
+ *         raise ValueError(
+ */
+  __pyx_t_2 = PyFloat_FromDouble((pow(2.0, ((double)(__pyx_v_bits_stored - 1))) - 1.0)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_11 = PyObject_RichCompare(__pyx_v_arr_max, __pyx_t_2, Py_GT); __Pyx_XGOTREF(__pyx_t_11); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  if (!__pyx_t_5) {
+  } else {
+    __pyx_t_6 = __pyx_t_5;
+    goto __pyx_L18_bool_binop_done;
+  }
+  __pyx_t_11 = PyFloat_FromDouble((-pow(2.0, ((double)(__pyx_v_bits_stored - 1))))); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_2 = PyObject_RichCompare(__pyx_v_arr_min, __pyx_t_11, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 273, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_6 = __pyx_t_5;
+  __pyx_L18_bool_binop_done:;
+
+  /* "_openjpeg.pyx":271
+ *         )
+ * 
+ *     if (             # <<<<<<<<<<<<<<
+ *         arr.dtype in (np.int8, np.int16, np.int32)
+ *         and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))
+ */
+  if (unlikely(__pyx_t_6)) {
+
+    /* "_openjpeg.pyx":276
+ *     ):
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "             # <<<<<<<<<<<<<<
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ *         )
+ */
+    __pyx_t_2 = PyTuple_New(7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 276, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __pyx_t_7 = 0;
+    __pyx_t_8 = 127;
+    __Pyx_INCREF(__pyx_kp_u_A_bits_stored_value_of);
+    __pyx_t_7 += 25;
+    __Pyx_GIVEREF(__pyx_kp_u_A_bits_stored_value_of);
+    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_kp_u_A_bits_stored_value_of);
+    __pyx_t_11 = __Pyx_PyUnicode_From_int(__pyx_v_bits_stored, 0, ' ', 'd'); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 276, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u_is_incompatible_with_the_range);
+    __pyx_t_7 += 67;
+    __Pyx_GIVEREF(__pyx_kp_u_is_incompatible_with_the_range);
+    PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u_is_incompatible_with_the_range);
+
+    /* "_openjpeg.pyx":277
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"             # <<<<<<<<<<<<<<
+ *         )
+ * 
+ */
+    __pyx_t_11 = __Pyx_PyObject_FormatSimple(__pyx_v_arr_min, __pyx_empty_unicode); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 277, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 3, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u__6);
+    __pyx_t_7 += 2;
+    __Pyx_GIVEREF(__pyx_kp_u__6);
+    PyTuple_SET_ITEM(__pyx_t_2, 4, __pyx_kp_u__6);
+    __pyx_t_11 = __Pyx_PyObject_FormatSimple(__pyx_v_arr_max, __pyx_empty_unicode); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 277, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __pyx_t_8 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) > __pyx_t_8) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_11) : __pyx_t_8;
+    __pyx_t_7 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_11);
+    __Pyx_GIVEREF(__pyx_t_11);
+    PyTuple_SET_ITEM(__pyx_t_2, 5, __pyx_t_11);
+    __pyx_t_11 = 0;
+    __Pyx_INCREF(__pyx_kp_u__4);
+    __pyx_t_7 += 1;
+    __Pyx_GIVEREF(__pyx_kp_u__4);
+    PyTuple_SET_ITEM(__pyx_t_2, 6, __pyx_kp_u__4);
+
+    /* "_openjpeg.pyx":276
+ *     ):
+ *         raise ValueError(
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "             # <<<<<<<<<<<<<<
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ *         )
+ */
+    __pyx_t_11 = __Pyx_PyUnicode_Join(__pyx_t_2, 7, __pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 276, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+    /* "_openjpeg.pyx":275
+ *         and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))
+ *     ):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             f"A 'bits_stored' value of {bits_stored} is incompatible with "
+ *             f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+ */
+    __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_ValueError, __pyx_t_11); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 275, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 275, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":271
+ *         )
+ * 
+ *     if (             # <<<<<<<<<<<<<<
+ *         arr.dtype in (np.int8, np.int16, np.int32)
+ *         and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))
+ */
+  }
+
+  /* "_openjpeg.pyx":281
+ * 
+ *     # MCT may be used with RGB in both lossy and lossless modes
+ *     use_mct = 1 if use_mct else 0             # <<<<<<<<<<<<<<
+ * 
+ *     if codec_format not in (0, 2):
+ */
+  if (__pyx_v_use_mct) {
+    __pyx_t_6 = 1;
+  } else {
+    __pyx_t_6 = 0;
+  }
+  __pyx_v_use_mct = __pyx_t_6;
+
+  /* "_openjpeg.pyx":283
+ *     use_mct = 1 if use_mct else 0
+ * 
+ *     if codec_format not in (0, 2):             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+ */
+  switch (__pyx_v_codec_format) {
+    case 0:
+    case 2:
+    __pyx_t_6 = 0;
+    break;
+    default:
+    __pyx_t_6 = 1;
+    break;
+  }
+  __pyx_t_5 = __pyx_t_6;
+  if (unlikely(__pyx_t_5)) {
+
+    /* "_openjpeg.pyx":284
+ * 
+ *     if codec_format not in (0, 2):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+ *         )
+ */
+    __pyx_t_2 = __Pyx_PyObject_Call(__pyx_builtin_ValueError, __pyx_tuple__7, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 284, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 284, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":283
+ *     use_mct = 1 if use_mct else 0
+ * 
+ *     if codec_format not in (0, 2):             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+ */
+  }
+
+  /* "_openjpeg.pyx":288
+ *         )
+ * 
+ *     compression_ratios = [float(x) for x in compression_ratios]             # <<<<<<<<<<<<<<
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+ *     if compression_ratios and signal_noise_ratios:
+ */
+  { /* enter inner scope */
+    __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 288, __pyx_L27_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    if (unlikely(__pyx_v_compression_ratios == Py_None)) {
+      PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable");
+      __PYX_ERR(0, 288, __pyx_L27_error)
+    }
+    __pyx_t_11 = __pyx_v_compression_ratios; __Pyx_INCREF(__pyx_t_11);
+    __pyx_t_7 = 0;
+    for (;;) {
+      {
+        Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_11);
+        #if !CYTHON_ASSUME_SAFE_MACROS
+        if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 288, __pyx_L27_error)
+        #endif
+        if (__pyx_t_7 >= __pyx_temp) break;
+      }
+      #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+      __pyx_t_10 = PyList_GET_ITEM(__pyx_t_11, __pyx_t_7); __Pyx_INCREF(__pyx_t_10); __pyx_t_7++; if (unlikely((0 < 0))) __PYX_ERR(0, 288, __pyx_L27_error)
+      #else
+      __pyx_t_10 = __Pyx_PySequence_ITEM(__pyx_t_11, __pyx_t_7); __pyx_t_7++; if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 288, __pyx_L27_error)
+      __Pyx_GOTREF(__pyx_t_10);
+      #endif
+      __Pyx_XDECREF_SET(__pyx_7genexpr__pyx_v_x, __pyx_t_10);
+      __pyx_t_10 = 0;
+      __pyx_t_10 = __Pyx_PyNumber_Float(__pyx_7genexpr__pyx_v_x); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 288, __pyx_L27_error)
+      __Pyx_GOTREF(__pyx_t_10);
+      if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_10))) __PYX_ERR(0, 288, __pyx_L27_error)
+      __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+    }
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __Pyx_XDECREF(__pyx_7genexpr__pyx_v_x); __pyx_7genexpr__pyx_v_x = 0;
+    goto __pyx_L31_exit_scope;
+    __pyx_L27_error:;
+    __Pyx_XDECREF(__pyx_7genexpr__pyx_v_x); __pyx_7genexpr__pyx_v_x = 0;
+    goto __pyx_L1_error;
+    __pyx_L31_exit_scope:;
+  } /* exit inner scope */
+  __Pyx_DECREF_SET(__pyx_v_compression_ratios, ((PyObject*)__pyx_t_2));
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":289
+ * 
+ *     compression_ratios = [float(x) for x in compression_ratios]
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]             # <<<<<<<<<<<<<<
+ *     if compression_ratios and signal_noise_ratios:
+ *         raise ValueError(
+ */
+  { /* enter inner scope */
+    __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 289, __pyx_L34_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    if (unlikely(__pyx_v_signal_noise_ratios == Py_None)) {
+      PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable");
+      __PYX_ERR(0, 289, __pyx_L34_error)
+    }
+    __pyx_t_11 = __pyx_v_signal_noise_ratios; __Pyx_INCREF(__pyx_t_11);
+    __pyx_t_7 = 0;
+    for (;;) {
+      {
+        Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_11);
+        #if !CYTHON_ASSUME_SAFE_MACROS
+        if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 289, __pyx_L34_error)
+        #endif
+        if (__pyx_t_7 >= __pyx_temp) break;
+      }
+      #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+      __pyx_t_10 = PyList_GET_ITEM(__pyx_t_11, __pyx_t_7); __Pyx_INCREF(__pyx_t_10); __pyx_t_7++; if (unlikely((0 < 0))) __PYX_ERR(0, 289, __pyx_L34_error)
+      #else
+      __pyx_t_10 = __Pyx_PySequence_ITEM(__pyx_t_11, __pyx_t_7); __pyx_t_7++; if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 289, __pyx_L34_error)
+      __Pyx_GOTREF(__pyx_t_10);
+      #endif
+      __Pyx_XDECREF_SET(__pyx_8genexpr1__pyx_v_x, __pyx_t_10);
+      __pyx_t_10 = 0;
+      __pyx_t_10 = __Pyx_PyNumber_Float(__pyx_8genexpr1__pyx_v_x); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 289, __pyx_L34_error)
+      __Pyx_GOTREF(__pyx_t_10);
+      if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_10))) __PYX_ERR(0, 289, __pyx_L34_error)
+      __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+    }
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+    __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_x); __pyx_8genexpr1__pyx_v_x = 0;
+    goto __pyx_L38_exit_scope;
+    __pyx_L34_error:;
+    __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_x); __pyx_8genexpr1__pyx_v_x = 0;
+    goto __pyx_L1_error;
+    __pyx_L38_exit_scope:;
+  } /* exit inner scope */
+  __Pyx_DECREF_SET(__pyx_v_signal_noise_ratios, ((PyObject*)__pyx_t_2));
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":290
+ *     compression_ratios = [float(x) for x in compression_ratios]
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+ *     if compression_ratios and signal_noise_ratios:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+ */
+  __pyx_t_6 = (PyList_GET_SIZE(__pyx_v_compression_ratios) != 0);
+  if (__pyx_t_6) {
+  } else {
+    __pyx_t_5 = __pyx_t_6;
+    goto __pyx_L40_bool_binop_done;
+  }
+  __pyx_t_6 = (PyList_GET_SIZE(__pyx_v_signal_noise_ratios) != 0);
+  __pyx_t_5 = __pyx_t_6;
+  __pyx_L40_bool_binop_done:;
+  if (unlikely(__pyx_t_5)) {
+
+    /* "_openjpeg.pyx":291
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+ *     if compression_ratios and signal_noise_ratios:
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+ *             "allowed when performing lossy compression"
+ */
+    __pyx_t_2 = __Pyx_PyObject_Call(__pyx_builtin_ValueError, __pyx_tuple__8, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 291, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 291, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":290
+ *     compression_ratios = [float(x) for x in compression_ratios]
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+ *     if compression_ratios and signal_noise_ratios:             # <<<<<<<<<<<<<<
+ *         raise ValueError(
+ *             "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+ */
+  }
+
+  /* "_openjpeg.pyx":295
+ *             "allowed when performing lossy compression"
+ *         )
+ *     if len(compression_ratios) > 10 or len(signal_noise_ratios) > 10:             # <<<<<<<<<<<<<<
+ *         raise ValueError("More than 10 compression layers is not supported")
+ * 
+ */
+  __pyx_t_7 = __Pyx_PyList_GET_SIZE(__pyx_v_compression_ratios); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 295, __pyx_L1_error)
+  __pyx_t_6 = (__pyx_t_7 > 10);
+  if (!__pyx_t_6) {
+  } else {
+    __pyx_t_5 = __pyx_t_6;
+    goto __pyx_L43_bool_binop_done;
+  }
+  __pyx_t_7 = __Pyx_PyList_GET_SIZE(__pyx_v_signal_noise_ratios); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 295, __pyx_L1_error)
+  __pyx_t_6 = (__pyx_t_7 > 10);
+  __pyx_t_5 = __pyx_t_6;
+  __pyx_L43_bool_binop_done:;
+  if (unlikely(__pyx_t_5)) {
+
+    /* "_openjpeg.pyx":296
+ *         )
+ *     if len(compression_ratios) > 10 or len(signal_noise_ratios) > 10:
+ *         raise ValueError("More than 10 compression layers is not supported")             # <<<<<<<<<<<<<<
+ * 
+ *     # The destination for the encoded J2K codestream, needs to support BinaryIO
+ */
+    __pyx_t_2 = __Pyx_PyObject_Call(__pyx_builtin_ValueError, __pyx_tuple__9, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 296, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_Raise(__pyx_t_2, 0, 0, 0);
+    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+    __PYX_ERR(0, 296, __pyx_L1_error)
+
+    /* "_openjpeg.pyx":295
+ *             "allowed when performing lossy compression"
+ *         )
+ *     if len(compression_ratios) > 10 or len(signal_noise_ratios) > 10:             # <<<<<<<<<<<<<<
+ *         raise ValueError("More than 10 compression layers is not supported")
+ * 
+ */
+  }
+
+  /* "_openjpeg.pyx":299
+ * 
+ *     # The destination for the encoded J2K codestream, needs to support BinaryIO
+ *     dst = BytesIO()             # <<<<<<<<<<<<<<
+ *     return_code = Encode(
+ *         <cnp.PyArrayObject *> arr,
+ */
+  __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_BytesIO); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 299, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_11);
+  __pyx_t_10 = NULL;
+  __pyx_t_12 = 0;
+  #if CYTHON_UNPACK_METHODS
+  if (unlikely(PyMethod_Check(__pyx_t_11))) {
+    __pyx_t_10 = PyMethod_GET_SELF(__pyx_t_11);
+    if (likely(__pyx_t_10)) {
+      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_11);
+      __Pyx_INCREF(__pyx_t_10);
+      __Pyx_INCREF(function);
+      __Pyx_DECREF_SET(__pyx_t_11, function);
+      __pyx_t_12 = 1;
+    }
+  }
+  #endif
+  {
+    PyObject *__pyx_callargs[2] = {__pyx_t_10, NULL};
+    __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_11, __pyx_callargs+1-__pyx_t_12, 0+__pyx_t_12);
+    __Pyx_XDECREF(__pyx_t_10); __pyx_t_10 = 0;
+    if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 299, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_2);
+    __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+  }
+  __pyx_v_dst = __pyx_t_2;
+  __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":300
+ *     # The destination for the encoded J2K codestream, needs to support BinaryIO
+ *     dst = BytesIO()
+ *     return_code = Encode(             # <<<<<<<<<<<<<<
+ *         <cnp.PyArrayObject *> arr,
+ *         <PyObject *> dst,
+ */
+  __pyx_v_return_code = Encode(((PyArrayObject *)__pyx_v_arr), ((PyObject *)__pyx_v_dst), __pyx_v_bits_stored, __pyx_v_photometric_interpretation, __pyx_v_use_mct, ((PyObject *)__pyx_v_compression_ratios), ((PyObject *)__pyx_v_signal_noise_ratios), __pyx_v_codec_format);
+
+  /* "_openjpeg.pyx":310
+ *         codec_format,
+ *     )
+ *     return return_code, dst.getvalue()             # <<<<<<<<<<<<<<
+ */
+  __Pyx_XDECREF(__pyx_r);
+  __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_return_code); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 310, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_v_dst, __pyx_n_s_getvalue); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 310, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __pyx_t_9 = NULL;
+  __pyx_t_12 = 0;
+  #if CYTHON_UNPACK_METHODS
+  if (likely(PyMethod_Check(__pyx_t_10))) {
+    __pyx_t_9 = PyMethod_GET_SELF(__pyx_t_10);
+    if (likely(__pyx_t_9)) {
+      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_10);
+      __Pyx_INCREF(__pyx_t_9);
+      __Pyx_INCREF(function);
+      __Pyx_DECREF_SET(__pyx_t_10, function);
+      __pyx_t_12 = 1;
+    }
+  }
+  #endif
+  {
+    PyObject *__pyx_callargs[2] = {__pyx_t_9, NULL};
+    __pyx_t_11 = __Pyx_PyObject_FastCall(__pyx_t_10, __pyx_callargs+1-__pyx_t_12, 0+__pyx_t_12);
+    __Pyx_XDECREF(__pyx_t_9); __pyx_t_9 = 0;
+    if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 310, __pyx_L1_error)
+    __Pyx_GOTREF(__pyx_t_11);
+    __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+  }
+  __pyx_t_10 = PyTuple_New(2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 310, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_10);
+  __Pyx_GIVEREF(__pyx_t_2);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 0, __pyx_t_2)) __PYX_ERR(0, 310, __pyx_L1_error);
+  __Pyx_GIVEREF(__pyx_t_11);
+  if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 1, __pyx_t_11)) __PYX_ERR(0, 310, __pyx_L1_error);
+  __pyx_t_2 = 0;
+  __pyx_t_11 = 0;
+  __pyx_r = ((PyObject*)__pyx_t_10);
+  __pyx_t_10 = 0;
+  goto __pyx_L0;
+
+  /* "_openjpeg.pyx":195
+ * 
+ * 
+ * def encode(             # <<<<<<<<<<<<<<
+ *     cnp.ndarray arr,
+ *     int bits_stored,
+ */
+
+  /* function exit code */
+  __pyx_L1_error:;
+  __Pyx_XDECREF(__pyx_t_1);
+  __Pyx_XDECREF(__pyx_t_2);
+  __Pyx_XDECREF(__pyx_t_3);
+  __Pyx_XDECREF(__pyx_t_4);
+  __Pyx_XDECREF(__pyx_t_9);
+  __Pyx_XDECREF(__pyx_t_10);
+  __Pyx_XDECREF(__pyx_t_11);
+  __Pyx_AddTraceback("_openjpeg.encode", __pyx_clineno, __pyx_lineno, __pyx_filename);
+  __pyx_r = NULL;
+  __pyx_L0:;
+  __Pyx_XDECREF(__pyx_v_allowed);
+  __Pyx_XDECREF(__pyx_v_arr_max);
+  __Pyx_XDECREF(__pyx_v_arr_min);
+  __Pyx_XDECREF(__pyx_v_dst);
+  __Pyx_XDECREF(__pyx_7genexpr__pyx_v_x);
+  __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_x);
+  __Pyx_XDECREF(__pyx_v_compression_ratios);
+  __Pyx_XDECREF(__pyx_v_signal_noise_ratios);
+  __Pyx_XGIVEREF(__pyx_r);
+  __Pyx_RefNannyFinishContext();
+  return __pyx_r;
+}
+
+static PyMethodDef __pyx_methods[] = {
+  {0, 0, 0, 0}
+};
+#ifndef CYTHON_SMALL_CODE
+#if defined(__clang__)
+    #define CYTHON_SMALL_CODE
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+    #define CYTHON_SMALL_CODE __attribute__((cold))
+#else
+    #define CYTHON_SMALL_CODE
+#endif
+#endif
+/* #### Code section: pystring_table ### */
+
+static int __Pyx_CreateStringTabAndInitStrings(void) {
+  __Pyx_StringTabEntry __pyx_string_tab[] = {
+    {&__pyx_kp_u_A_bits_stored_value_of, __pyx_k_A_bits_stored_value_of, sizeof(__pyx_k_A_bits_stored_value_of), 0, 1, 0, 0},
+    {&__pyx_n_s_BinaryIO, __pyx_k_BinaryIO, sizeof(__pyx_k_BinaryIO), 0, 0, 1, 1},
+    {&__pyx_n_s_BytesIO, __pyx_k_BytesIO, sizeof(__pyx_k_BytesIO), 0, 0, 1, 1},
+    {&__pyx_n_u_CYMK, __pyx_k_CYMK, sizeof(__pyx_k_CYMK), 0, 1, 0, 1},
+    {&__pyx_n_s_Dict, __pyx_k_Dict, sizeof(__pyx_k_Dict), 0, 0, 1, 1},
+    {&__pyx_kp_s_Dict_str_Union_str_int_bool, __pyx_k_Dict_str_Union_str_int_bool, sizeof(__pyx_k_Dict_str_Union_str_int_bool), 0, 0, 1, 0},
+    {&__pyx_n_s_ERRORS, __pyx_k_ERRORS, sizeof(__pyx_k_ERRORS), 0, 0, 1, 1},
+    {&__pyx_kp_u_Error_decoding_the_J2K_data, __pyx_k_Error_decoding_the_J2K_data, sizeof(__pyx_k_Error_decoding_the_J2K_data), 0, 1, 0, 0},
+    {&__pyx_n_s_ImportError, __pyx_k_ImportError, sizeof(__pyx_k_ImportError), 0, 0, 1, 1},
+    {&__pyx_kp_u_Invalid_value_for_the_bits_store, __pyx_k_Invalid_value_for_the_bits_store, sizeof(__pyx_k_Invalid_value_for_the_bits_store), 0, 1, 0, 0},
+    {&__pyx_n_s_KeyError, __pyx_k_KeyError, sizeof(__pyx_k_KeyError), 0, 0, 1, 1},
+    {&__pyx_n_s_LOGGER, __pyx_k_LOGGER, sizeof(__pyx_k_LOGGER), 0, 0, 1, 1},
+    {&__pyx_n_s_List, __pyx_k_List, sizeof(__pyx_k_List), 0, 0, 1, 1},
+    {&__pyx_kp_u_More_than_10_compression_layers, __pyx_k_More_than_10_compression_layers, sizeof(__pyx_k_More_than_10_compression_layers), 0, 1, 0, 0},
+    {&__pyx_kp_u_Only_one_of_compression_ratios_o, __pyx_k_Only_one_of_compression_ratios_o, sizeof(__pyx_k_Only_one_of_compression_ratios_o), 0, 1, 0, 0},
+    {&__pyx_n_s_RuntimeError, __pyx_k_RuntimeError, sizeof(__pyx_k_RuntimeError), 0, 0, 1, 1},
+    {&__pyx_kp_u_The_input_array_contains_values, __pyx_k_The_input_array_contains_values, sizeof(__pyx_k_The_input_array_contains_values), 0, 1, 0, 0},
+    {&__pyx_kp_u_The_input_array_has_an_unsupport, __pyx_k_The_input_array_has_an_unsupport, sizeof(__pyx_k_The_input_array_has_an_unsupport), 0, 1, 0, 0},
+    {&__pyx_kp_u_The_value_of_the_codec_format_pa, __pyx_k_The_value_of_the_codec_format_pa, sizeof(__pyx_k_The_value_of_the_codec_format_pa), 0, 1, 0, 0},
+    {&__pyx_n_s_Tuple, __pyx_k_Tuple, sizeof(__pyx_k_Tuple), 0, 0, 1, 1},
+    {&__pyx_kp_s_Tuple_int_bytes, __pyx_k_Tuple_int_bytes, sizeof(__pyx_k_Tuple_int_bytes), 0, 0, 1, 0},
+    {&__pyx_n_s_Union, __pyx_k_Union, sizeof(__pyx_k_Union), 0, 0, 1, 1},
+    {&__pyx_kp_s_Union_np_ndarray_bytearray, __pyx_k_Union_np_ndarray_bytearray, sizeof(__pyx_k_Union_np_ndarray_bytearray), 0, 0, 1, 0},
+    {&__pyx_n_s_ValueError, __pyx_k_ValueError, sizeof(__pyx_k_ValueError), 0, 0, 1, 1},
+    {&__pyx_n_u_YUV, __pyx_k_YUV, sizeof(__pyx_k_YUV), 0, 1, 0, 1},
+    {&__pyx_kp_u__10, __pyx_k__10, sizeof(__pyx_k__10), 0, 1, 0, 0},
+    {&__pyx_n_s__11, __pyx_k__11, sizeof(__pyx_k__11), 0, 0, 1, 1},
+    {&__pyx_n_s__20, __pyx_k__20, sizeof(__pyx_k__20), 0, 0, 1, 1},
+    {&__pyx_kp_u__3, __pyx_k__3, sizeof(__pyx_k__3), 0, 1, 0, 0},
+    {&__pyx_kp_u__4, __pyx_k__4, sizeof(__pyx_k__4), 0, 1, 0, 0},
+    {&__pyx_kp_u__6, __pyx_k__6, sizeof(__pyx_k__6), 0, 1, 0, 0},
+    {&__pyx_n_s_allowed, __pyx_k_allowed, sizeof(__pyx_k_allowed), 0, 0, 1, 1},
+    {&__pyx_n_s_arr, __pyx_k_arr, sizeof(__pyx_k_arr), 0, 0, 1, 1},
+    {&__pyx_n_s_arr_max, __pyx_k_arr_max, sizeof(__pyx_k_arr_max), 0, 0, 1, 1},
+    {&__pyx_n_s_arr_min, __pyx_k_arr_min, sizeof(__pyx_k_arr_min), 0, 0, 1, 1},
+    {&__pyx_n_s_as_array, __pyx_k_as_array, sizeof(__pyx_k_as_array), 0, 0, 1, 1},
+    {&__pyx_n_s_asyncio_coroutines, __pyx_k_asyncio_coroutines, sizeof(__pyx_k_asyncio_coroutines), 0, 0, 1, 1},
+    {&__pyx_n_s_bits_stored, __pyx_k_bits_stored, sizeof(__pyx_k_bits_stored), 0, 0, 1, 1},
+    {&__pyx_n_s_bool, __pyx_k_bool, sizeof(__pyx_k_bool), 0, 0, 1, 1},
+    {&__pyx_n_s_bpp, __pyx_k_bpp, sizeof(__pyx_k_bpp), 0, 0, 1, 1},
+    {&__pyx_n_s_bytes, __pyx_k_bytes, sizeof(__pyx_k_bytes), 0, 0, 1, 1},
+    {&__pyx_n_s_ceil, __pyx_k_ceil, sizeof(__pyx_k_ceil), 0, 0, 1, 1},
+    {&__pyx_n_s_cline_in_traceback, __pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 0, 1, 1},
+    {&__pyx_n_s_codec, __pyx_k_codec, sizeof(__pyx_k_codec), 0, 0, 1, 1},
+    {&__pyx_n_s_codec_format, __pyx_k_codec_format, sizeof(__pyx_k_codec_format), 0, 0, 1, 1},
+    {&__pyx_n_s_colours, __pyx_k_colours, sizeof(__pyx_k_colours), 0, 0, 1, 1},
+    {&__pyx_n_s_colourspace, __pyx_k_colourspace, sizeof(__pyx_k_colourspace), 0, 0, 1, 1},
+    {&__pyx_n_u_colourspace, __pyx_k_colourspace, sizeof(__pyx_k_colourspace), 0, 1, 0, 1},
+    {&__pyx_n_u_columns, __pyx_k_columns, sizeof(__pyx_k_columns), 0, 1, 0, 1},
+    {&__pyx_n_s_compression_ratios, __pyx_k_compression_ratios, sizeof(__pyx_k_compression_ratios), 0, 0, 1, 1},
+    {&__pyx_n_s_decode, __pyx_k_decode, sizeof(__pyx_k_decode), 0, 0, 1, 1},
+    {&__pyx_n_s_dst, __pyx_k_dst, sizeof(__pyx_k_dst), 0, 0, 1, 1},
+    {&__pyx_n_s_dtype, __pyx_k_dtype, sizeof(__pyx_k_dtype), 0, 0, 1, 1},
+    {&__pyx_kp_u_e_YCC, __pyx_k_e_YCC, sizeof(__pyx_k_e_YCC), 0, 1, 0, 0},
+    {&__pyx_n_s_encode, __pyx_k_encode, sizeof(__pyx_k_encode), 0, 0, 1, 1},
+    {&__pyx_kp_u_failed_to_create_the_input_strea, __pyx_k_failed_to_create_the_input_strea, sizeof(__pyx_k_failed_to_create_the_input_strea), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_decode_image, __pyx_k_failed_to_decode_image, sizeof(__pyx_k_failed_to_decode_image), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_read_the_header, __pyx_k_failed_to_read_the_header, sizeof(__pyx_k_failed_to_read_the_header), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_set_the_component_indi, __pyx_k_failed_to_set_the_component_indi, sizeof(__pyx_k_failed_to_set_the_component_indi), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_set_the_decoded_area, __pyx_k_failed_to_set_the_decoded_area, sizeof(__pyx_k_failed_to_set_the_decoded_area), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_setup_the_decoder, __pyx_k_failed_to_setup_the_decoder, sizeof(__pyx_k_failed_to_setup_the_decoder), 0, 1, 0, 0},
+    {&__pyx_kp_u_failed_to_upscale_subsampled_com, __pyx_k_failed_to_upscale_subsampled_com, sizeof(__pyx_k_failed_to_upscale_subsampled_com), 0, 1, 0, 0},
+    {&__pyx_n_s_fp, __pyx_k_fp, sizeof(__pyx_k_fp), 0, 0, 1, 1},
+    {&__pyx_n_s_getLogger, __pyx_k_getLogger, sizeof(__pyx_k_getLogger), 0, 0, 1, 1},
+    {&__pyx_n_s_get_parameters, __pyx_k_get_parameters, sizeof(__pyx_k_get_parameters), 0, 0, 1, 1},
+    {&__pyx_n_s_get_version, __pyx_k_get_version, sizeof(__pyx_k_get_version), 0, 0, 1, 1},
+    {&__pyx_n_s_getvalue, __pyx_k_getvalue, sizeof(__pyx_k_getvalue), 0, 0, 1, 1},
+    {&__pyx_n_s_import, __pyx_k_import, sizeof(__pyx_k_import), 0, 0, 1, 1},
+    {&__pyx_n_s_initializing, __pyx_k_initializing, sizeof(__pyx_k_initializing), 0, 0, 1, 1},
+    {&__pyx_n_s_int, __pyx_k_int, sizeof(__pyx_k_int), 0, 0, 1, 1},
+    {&__pyx_n_s_int16, __pyx_k_int16, sizeof(__pyx_k_int16), 0, 0, 1, 1},
+    {&__pyx_n_s_int32, __pyx_k_int32, sizeof(__pyx_k_int32), 0, 0, 1, 1},
+    {&__pyx_n_s_int8, __pyx_k_int8, sizeof(__pyx_k_int8), 0, 0, 1, 1},
+    {&__pyx_n_s_io, __pyx_k_io, sizeof(__pyx_k_io), 0, 0, 1, 1},
+    {&__pyx_n_s_is_coroutine, __pyx_k_is_coroutine, sizeof(__pyx_k_is_coroutine), 0, 0, 1, 1},
+    {&__pyx_kp_u_is_incompatible_with_the_range, __pyx_k_is_incompatible_with_the_range, sizeof(__pyx_k_is_incompatible_with_the_range), 0, 1, 0, 0},
     {&__pyx_n_u_is_signed, __pyx_k_is_signed, sizeof(__pyx_k_is_signed), 0, 1, 0, 1},
+    {&__pyx_n_s_itemsize, __pyx_k_itemsize, sizeof(__pyx_k_itemsize), 0, 0, 1, 1},
+    {&__pyx_n_s_logging, __pyx_k_logging, sizeof(__pyx_k_logging), 0, 0, 1, 1},
     {&__pyx_n_s_main, __pyx_k_main, sizeof(__pyx_k_main), 0, 0, 1, 1},
     {&__pyx_n_s_math, __pyx_k_math, sizeof(__pyx_k_math), 0, 0, 1, 1},
+    {&__pyx_n_s_max, __pyx_k_max, sizeof(__pyx_k_max), 0, 0, 1, 1},
+    {&__pyx_n_s_min, __pyx_k_min, sizeof(__pyx_k_min), 0, 0, 1, 1},
     {&__pyx_n_u_monochrome, __pyx_k_monochrome, sizeof(__pyx_k_monochrome), 0, 1, 0, 1},
     {&__pyx_n_s_msg, __pyx_k_msg, sizeof(__pyx_k_msg), 0, 0, 1, 1},
     {&__pyx_n_s_name, __pyx_k_name, sizeof(__pyx_k_name), 0, 0, 1, 1},
@@ -5991,6 +7775,7 @@ static int __Pyx_CreateStringTabAndInitStrings(void) {
     {&__pyx_n_s_numpy, __pyx_k_numpy, sizeof(__pyx_k_numpy), 0, 0, 1, 1},
     {&__pyx_kp_u_numpy_core_multiarray_failed_to, __pyx_k_numpy_core_multiarray_failed_to, sizeof(__pyx_k_numpy_core_multiarray_failed_to), 0, 1, 0, 0},
     {&__pyx_kp_u_numpy_core_umath_failed_to_impor, __pyx_k_numpy_core_umath_failed_to_impor, sizeof(__pyx_k_numpy_core_umath_failed_to_impor), 0, 1, 0, 0},
+    {&__pyx_kp_u_only_bool_u1_u2_u4_i1_i2_and_i4, __pyx_k_only_bool_u1_u2_u4_i1_i2_and_i4, sizeof(__pyx_k_only_bool_u1_u2_u4_i1_i2_and_i4), 0, 1, 0, 0},
     {&__pyx_n_s_openjpeg, __pyx_k_openjpeg, sizeof(__pyx_k_openjpeg), 0, 0, 1, 1},
     {&__pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_k_openjpeg__openjpeg_pyx, sizeof(__pyx_k_openjpeg__openjpeg_pyx), 0, 0, 1, 0},
     {&__pyx_n_s_out, __pyx_k_out, sizeof(__pyx_k_out), 0, 0, 1, 1},
@@ -5999,20 +7784,27 @@ static int __Pyx_CreateStringTabAndInitStrings(void) {
     {&__pyx_n_s_p_param, __pyx_k_p_param, sizeof(__pyx_k_p_param), 0, 0, 1, 1},
     {&__pyx_n_s_param, __pyx_k_param, sizeof(__pyx_k_param), 0, 0, 1, 1},
     {&__pyx_n_s_parameters, __pyx_k_parameters, sizeof(__pyx_k_parameters), 0, 0, 1, 1},
+    {&__pyx_n_s_photometric_interpretation, __pyx_k_photometric_interpretation, sizeof(__pyx_k_photometric_interpretation), 0, 0, 1, 1},
     {&__pyx_n_u_precision, __pyx_k_precision, sizeof(__pyx_k_precision), 0, 1, 0, 1},
     {&__pyx_n_s_ptr, __pyx_k_ptr, sizeof(__pyx_k_ptr), 0, 0, 1, 1},
     {&__pyx_n_s_result, __pyx_k_result, sizeof(__pyx_k_result), 0, 0, 1, 1},
     {&__pyx_n_s_return, __pyx_k_return, sizeof(__pyx_k_return), 0, 0, 1, 1},
+    {&__pyx_n_s_return_code, __pyx_k_return_code, sizeof(__pyx_k_return_code), 0, 0, 1, 1},
     {&__pyx_n_u_rows, __pyx_k_rows, sizeof(__pyx_k_rows), 0, 1, 0, 1},
     {&__pyx_n_u_sRGB, __pyx_k_sRGB, sizeof(__pyx_k_sRGB), 0, 1, 0, 1},
+    {&__pyx_n_s_signal_noise_ratios, __pyx_k_signal_noise_ratios, sizeof(__pyx_k_signal_noise_ratios), 0, 0, 1, 1},
     {&__pyx_n_s_spec, __pyx_k_spec, sizeof(__pyx_k_spec), 0, 0, 1, 1},
     {&__pyx_kp_u_support_for_more_than_16_bits_pe, __pyx_k_support_for_more_than_16_bits_pe, sizeof(__pyx_k_support_for_more_than_16_bits_pe), 0, 1, 0, 0},
     {&__pyx_n_s_test, __pyx_k_test, sizeof(__pyx_k_test), 0, 0, 1, 1},
     {&__pyx_n_s_typing, __pyx_k_typing, sizeof(__pyx_k_typing), 0, 0, 1, 1},
+    {&__pyx_n_s_uint16, __pyx_k_uint16, sizeof(__pyx_k_uint16), 0, 0, 1, 1},
+    {&__pyx_n_s_uint32, __pyx_k_uint32, sizeof(__pyx_k_uint32), 0, 0, 1, 1},
     {&__pyx_n_s_uint8, __pyx_k_uint8, sizeof(__pyx_k_uint8), 0, 0, 1, 1},
     {&__pyx_n_u_unknown, __pyx_k_unknown, sizeof(__pyx_k_unknown), 0, 1, 0, 1},
     {&__pyx_n_u_unspecified, __pyx_k_unspecified, sizeof(__pyx_k_unspecified), 0, 1, 0, 1},
+    {&__pyx_n_s_use_mct, __pyx_k_use_mct, sizeof(__pyx_k_use_mct), 0, 0, 1, 1},
     {&__pyx_n_s_version, __pyx_k_version, sizeof(__pyx_k_version), 0, 0, 1, 1},
+    {&__pyx_n_s_x, __pyx_k_x, sizeof(__pyx_k_x), 0, 0, 1, 1},
     {&__pyx_n_s_zeros, __pyx_k_zeros, sizeof(__pyx_k_zeros), 0, 0, 1, 1},
     {0, 0, 0, 0, 0, 0, 0}
   };
@@ -6020,8 +7812,9 @@ static int __Pyx_CreateStringTabAndInitStrings(void) {
 }
 /* #### Code section: cached_builtins ### */
 static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) {
-  __pyx_builtin_RuntimeError = __Pyx_GetBuiltinName(__pyx_n_s_RuntimeError); if (!__pyx_builtin_RuntimeError) __PYX_ERR(0, 94, __pyx_L1_error)
-  __pyx_builtin_KeyError = __Pyx_GetBuiltinName(__pyx_n_s_KeyError); if (!__pyx_builtin_KeyError) __PYX_ERR(0, 149, __pyx_L1_error)
+  __pyx_builtin_KeyError = __Pyx_GetBuiltinName(__pyx_n_s_KeyError); if (!__pyx_builtin_KeyError) __PYX_ERR(0, 161, __pyx_L1_error)
+  __pyx_builtin_RuntimeError = __Pyx_GetBuiltinName(__pyx_n_s_RuntimeError); if (!__pyx_builtin_RuntimeError) __PYX_ERR(0, 164, __pyx_L1_error)
+  __pyx_builtin_ValueError = __Pyx_GetBuiltinName(__pyx_n_s_ValueError); if (!__pyx_builtin_ValueError) __PYX_ERR(0, 238, __pyx_L1_error)
   __pyx_builtin_ImportError = __Pyx_GetBuiltinName(__pyx_n_s_ImportError); if (!__pyx_builtin_ImportError) __PYX_ERR(1, 986, __pyx_L1_error)
   return 0;
   __pyx_L1_error:;
@@ -6033,7 +7826,7 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
   __Pyx_RefNannyDeclarations
   __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":986
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":986
  *         __pyx_import_array()
  *     except Exception:
  *         raise ImportError("numpy.core.multiarray failed to import")             # <<<<<<<<<<<<<<
@@ -6044,7 +7837,7 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
   __Pyx_GOTREF(__pyx_tuple_);
   __Pyx_GIVEREF(__pyx_tuple_);
 
-  /* "../../../../../tmp/pip-build-env-1f1oalyc/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":992
+  /* "../../../../../tmp/pip-build-env-k1t5uzrx/overlay/lib/python3.8/site-packages/numpy/__init__.cython-30.pxd":992
  *         _import_umath()
  *     except Exception:
  *         raise ImportError("numpy.core.umath failed to import")             # <<<<<<<<<<<<<<
@@ -6055,41 +7848,97 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
   __Pyx_GOTREF(__pyx_tuple__2);
   __Pyx_GIVEREF(__pyx_tuple__2);
 
-  /* "_openjpeg.pyx":38
+  /* "_openjpeg.pyx":259
+ *         or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+ *     ):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "The input array contains values outside the range of the maximum "
+ *             "supported bit-depth of 24"
+ */
+  __pyx_tuple__5 = PyTuple_Pack(1, __pyx_kp_u_The_input_array_contains_values); if (unlikely(!__pyx_tuple__5)) __PYX_ERR(0, 259, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__5);
+  __Pyx_GIVEREF(__pyx_tuple__5);
+
+  /* "_openjpeg.pyx":284
+ * 
+ *     if codec_format not in (0, 2):
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+ *         )
+ */
+  __pyx_tuple__7 = PyTuple_Pack(1, __pyx_kp_u_The_value_of_the_codec_format_pa); if (unlikely(!__pyx_tuple__7)) __PYX_ERR(0, 284, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__7);
+  __Pyx_GIVEREF(__pyx_tuple__7);
+
+  /* "_openjpeg.pyx":291
+ *     signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+ *     if compression_ratios and signal_noise_ratios:
+ *         raise ValueError(             # <<<<<<<<<<<<<<
+ *             "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+ *             "allowed when performing lossy compression"
+ */
+  __pyx_tuple__8 = PyTuple_Pack(1, __pyx_kp_u_Only_one_of_compression_ratios_o); if (unlikely(!__pyx_tuple__8)) __PYX_ERR(0, 291, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__8);
+  __Pyx_GIVEREF(__pyx_tuple__8);
+
+  /* "_openjpeg.pyx":296
+ *         )
+ *     if len(compression_ratios) > 10 or len(signal_noise_ratios) > 10:
+ *         raise ValueError("More than 10 compression layers is not supported")             # <<<<<<<<<<<<<<
+ * 
+ *     # The destination for the encoded J2K codestream, needs to support BinaryIO
+ */
+  __pyx_tuple__9 = PyTuple_Pack(1, __pyx_kp_u_More_than_10_compression_layers); if (unlikely(!__pyx_tuple__9)) __PYX_ERR(0, 296, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__9);
+  __Pyx_GIVEREF(__pyx_tuple__9);
+
+  /* "_openjpeg.pyx":52
  * 
  * 
  * def get_version() -> bytes:             # <<<<<<<<<<<<<<
  *     """Return the openjpeg version as bytes."""
  *     cdef char *version = OpenJpegVersion()
  */
-  __pyx_tuple__6 = PyTuple_Pack(1, __pyx_n_s_version); if (unlikely(!__pyx_tuple__6)) __PYX_ERR(0, 38, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_tuple__6);
-  __Pyx_GIVEREF(__pyx_tuple__6);
-  __pyx_codeobj__7 = (PyObject*)__Pyx_PyCode_New(0, 0, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__6, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_get_version, 38, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__7)) __PYX_ERR(0, 38, __pyx_L1_error)
+  __pyx_tuple__12 = PyTuple_Pack(1, __pyx_n_s_version); if (unlikely(!__pyx_tuple__12)) __PYX_ERR(0, 52, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__12);
+  __Pyx_GIVEREF(__pyx_tuple__12);
+  __pyx_codeobj__13 = (PyObject*)__Pyx_PyCode_New(0, 0, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__12, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_get_version, 52, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__13)) __PYX_ERR(0, 52, __pyx_L1_error)
 
-  /* "_openjpeg.pyx":45
+  /* "_openjpeg.pyx":59
  * 
  * 
  * def decode(             # <<<<<<<<<<<<<<
  *     fp: BinaryIO,
  *     codec: int = 0,
  */
-  __pyx_tuple__8 = PyTuple_Pack(10, __pyx_n_s_fp, __pyx_n_s_codec, __pyx_n_s_as_array, __pyx_n_s_param, __pyx_n_s_bpp, __pyx_n_s_nr_bytes, __pyx_n_s_p_in, __pyx_n_s_p_out, __pyx_n_s_out, __pyx_n_s_result); if (unlikely(!__pyx_tuple__8)) __PYX_ERR(0, 45, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_tuple__8);
-  __Pyx_GIVEREF(__pyx_tuple__8);
-  __pyx_codeobj__9 = (PyObject*)__Pyx_PyCode_New(3, 0, 0, 10, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__8, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_decode, 45, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__9)) __PYX_ERR(0, 45, __pyx_L1_error)
+  __pyx_tuple__14 = PyTuple_Pack(10, __pyx_n_s_fp, __pyx_n_s_codec, __pyx_n_s_as_array, __pyx_n_s_param, __pyx_n_s_bpp, __pyx_n_s_nr_bytes, __pyx_n_s_p_in, __pyx_n_s_p_out, __pyx_n_s_out, __pyx_n_s_return_code); if (unlikely(!__pyx_tuple__14)) __PYX_ERR(0, 59, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__14);
+  __Pyx_GIVEREF(__pyx_tuple__14);
+  __pyx_codeobj__15 = (PyObject*)__Pyx_PyCode_New(3, 0, 0, 10, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__14, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_decode, 59, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__15)) __PYX_ERR(0, 59, __pyx_L1_error)
 
-  /* "_openjpeg.pyx":101
+  /* "_openjpeg.pyx":113
  * 
  * 
  * def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bool]]:             # <<<<<<<<<<<<<<
  *     """Return a :class:`dict` containing the JPEG 2000 image parameters.
  * 
  */
-  __pyx_tuple__10 = PyTuple_Pack(10, __pyx_n_s_fp, __pyx_n_s_codec, __pyx_n_s_param, __pyx_n_s_p_param, __pyx_n_s_ptr, __pyx_n_s_result, __pyx_n_s_msg, __pyx_n_s_colours, __pyx_n_s_colourspace, __pyx_n_s_parameters); if (unlikely(!__pyx_tuple__10)) __PYX_ERR(0, 101, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_tuple__10);
-  __Pyx_GIVEREF(__pyx_tuple__10);
-  __pyx_codeobj__11 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 10, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__10, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_get_parameters, 101, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__11)) __PYX_ERR(0, 101, __pyx_L1_error)
+  __pyx_tuple__16 = PyTuple_Pack(10, __pyx_n_s_fp, __pyx_n_s_codec, __pyx_n_s_param, __pyx_n_s_p_param, __pyx_n_s_ptr, __pyx_n_s_result, __pyx_n_s_msg, __pyx_n_s_colours, __pyx_n_s_colourspace, __pyx_n_s_parameters); if (unlikely(!__pyx_tuple__16)) __PYX_ERR(0, 113, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__16);
+  __Pyx_GIVEREF(__pyx_tuple__16);
+  __pyx_codeobj__17 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 10, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__16, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_get_parameters, 113, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__17)) __PYX_ERR(0, 113, __pyx_L1_error)
+
+  /* "_openjpeg.pyx":195
+ * 
+ * 
+ * def encode(             # <<<<<<<<<<<<<<
+ *     cnp.ndarray arr,
+ *     int bits_stored,
+ */
+  __pyx_tuple__18 = PyTuple_Pack(14, __pyx_n_s_arr, __pyx_n_s_bits_stored, __pyx_n_s_photometric_interpretation, __pyx_n_s_use_mct, __pyx_n_s_compression_ratios, __pyx_n_s_signal_noise_ratios, __pyx_n_s_codec_format, __pyx_n_s_allowed, __pyx_n_s_arr_max, __pyx_n_s_arr_min, __pyx_n_s_dst, __pyx_n_s_return_code, __pyx_n_s_x, __pyx_n_s_x); if (unlikely(!__pyx_tuple__18)) __PYX_ERR(0, 195, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__18);
+  __Pyx_GIVEREF(__pyx_tuple__18);
+  __pyx_codeobj__19 = (PyObject*)__Pyx_PyCode_New(7, 0, 0, 14, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__18, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_openjpeg__openjpeg_pyx, __pyx_n_s_encode, 195, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__19)) __PYX_ERR(0, 195, __pyx_L1_error)
   __Pyx_RefNannyFinishContext();
   return 0;
   __pyx_L1_error:;
@@ -6109,7 +7958,10 @@ static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {
   __pyx_int_6 = PyInt_FromLong(6); if (unlikely(!__pyx_int_6)) __PYX_ERR(0, 1, __pyx_L1_error)
   __pyx_int_7 = PyInt_FromLong(7); if (unlikely(!__pyx_int_7)) __PYX_ERR(0, 1, __pyx_L1_error)
   __pyx_int_8 = PyInt_FromLong(8); if (unlikely(!__pyx_int_8)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __pyx_int_8388607 = PyInt_FromLong(8388607L); if (unlikely(!__pyx_int_8388607)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __pyx_int_16777215 = PyInt_FromLong(16777215L); if (unlikely(!__pyx_int_16777215)) __PYX_ERR(0, 1, __pyx_L1_error)
   __pyx_int_neg_1 = PyInt_FromLong(-1); if (unlikely(!__pyx_int_neg_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __pyx_int_neg_8388608 = PyInt_FromLong(-8388608L); if (unlikely(!__pyx_int_neg_8388608)) __PYX_ERR(0, 1, __pyx_L1_error)
   return 0;
   __pyx_L1_error:;
   return -1;
@@ -6192,33 +8044,33 @@ static int __Pyx_modinit_type_import_code(void) {
   /*--- Type import code ---*/
   __pyx_t_1 = PyImport_ImportModule(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_t_1)) __PYX_ERR(2, 9, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
-  __pyx_ptype_7cpython_4type_type = __Pyx_ImportType_3_0_7(__pyx_t_1, __Pyx_BUILTIN_MODULE_NAME, "type", 
+  __pyx_ptype_7cpython_4type_type = __Pyx_ImportType_3_0_8(__pyx_t_1, __Pyx_BUILTIN_MODULE_NAME, "type", 
   #if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x050B0000
-  sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyTypeObject),
+  sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyTypeObject),
   #elif CYTHON_COMPILING_IN_LIMITED_API
-  sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyTypeObject),
+  sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyTypeObject),
   #else
-  sizeof(PyHeapTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyHeapTypeObject),
+  sizeof(PyHeapTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyHeapTypeObject),
   #endif
-  __Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_7cpython_4type_type) __PYX_ERR(2, 9, __pyx_L1_error)
+  __Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_7cpython_4type_type) __PYX_ERR(2, 9, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
   __pyx_t_1 = PyImport_ImportModule("numpy"); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 202, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_1);
-  __pyx_ptype_5numpy_dtype = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "dtype", sizeof(PyArray_Descr), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyArray_Descr),__Pyx_ImportType_CheckSize_Ignore_3_0_7); if (!__pyx_ptype_5numpy_dtype) __PYX_ERR(1, 202, __pyx_L1_error)
-  __pyx_ptype_5numpy_flatiter = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "flatiter", sizeof(PyArrayIterObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyArrayIterObject),__Pyx_ImportType_CheckSize_Ignore_3_0_7); if (!__pyx_ptype_5numpy_flatiter) __PYX_ERR(1, 225, __pyx_L1_error)
-  __pyx_ptype_5numpy_broadcast = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "broadcast", sizeof(PyArrayMultiIterObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyArrayMultiIterObject),__Pyx_ImportType_CheckSize_Ignore_3_0_7); if (!__pyx_ptype_5numpy_broadcast) __PYX_ERR(1, 229, __pyx_L1_error)
-  __pyx_ptype_5numpy_ndarray = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "ndarray", sizeof(PyArrayObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyArrayObject),__Pyx_ImportType_CheckSize_Ignore_3_0_7); if (!__pyx_ptype_5numpy_ndarray) __PYX_ERR(1, 238, __pyx_L1_error)
-  __pyx_ptype_5numpy_generic = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "generic", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_generic) __PYX_ERR(1, 812, __pyx_L1_error)
-  __pyx_ptype_5numpy_number = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "number", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_number) __PYX_ERR(1, 814, __pyx_L1_error)
-  __pyx_ptype_5numpy_integer = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "integer", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_integer) __PYX_ERR(1, 816, __pyx_L1_error)
-  __pyx_ptype_5numpy_signedinteger = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "signedinteger", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_signedinteger) __PYX_ERR(1, 818, __pyx_L1_error)
-  __pyx_ptype_5numpy_unsignedinteger = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "unsignedinteger", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_unsignedinteger) __PYX_ERR(1, 820, __pyx_L1_error)
-  __pyx_ptype_5numpy_inexact = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "inexact", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_inexact) __PYX_ERR(1, 822, __pyx_L1_error)
-  __pyx_ptype_5numpy_floating = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "floating", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_floating) __PYX_ERR(1, 824, __pyx_L1_error)
-  __pyx_ptype_5numpy_complexfloating = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "complexfloating", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_complexfloating) __PYX_ERR(1, 826, __pyx_L1_error)
-  __pyx_ptype_5numpy_flexible = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "flexible", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_flexible) __PYX_ERR(1, 828, __pyx_L1_error)
-  __pyx_ptype_5numpy_character = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "character", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_7); if (!__pyx_ptype_5numpy_character) __PYX_ERR(1, 830, __pyx_L1_error)
-  __pyx_ptype_5numpy_ufunc = __Pyx_ImportType_3_0_7(__pyx_t_1, "numpy", "ufunc", sizeof(PyUFuncObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_7(PyUFuncObject),__Pyx_ImportType_CheckSize_Ignore_3_0_7); if (!__pyx_ptype_5numpy_ufunc) __PYX_ERR(1, 868, __pyx_L1_error)
+  __pyx_ptype_5numpy_dtype = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "dtype", sizeof(PyArray_Descr), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyArray_Descr),__Pyx_ImportType_CheckSize_Ignore_3_0_8); if (!__pyx_ptype_5numpy_dtype) __PYX_ERR(1, 202, __pyx_L1_error)
+  __pyx_ptype_5numpy_flatiter = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "flatiter", sizeof(PyArrayIterObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyArrayIterObject),__Pyx_ImportType_CheckSize_Ignore_3_0_8); if (!__pyx_ptype_5numpy_flatiter) __PYX_ERR(1, 225, __pyx_L1_error)
+  __pyx_ptype_5numpy_broadcast = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "broadcast", sizeof(PyArrayMultiIterObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyArrayMultiIterObject),__Pyx_ImportType_CheckSize_Ignore_3_0_8); if (!__pyx_ptype_5numpy_broadcast) __PYX_ERR(1, 229, __pyx_L1_error)
+  __pyx_ptype_5numpy_ndarray = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "ndarray", sizeof(PyArrayObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyArrayObject),__Pyx_ImportType_CheckSize_Ignore_3_0_8); if (!__pyx_ptype_5numpy_ndarray) __PYX_ERR(1, 238, __pyx_L1_error)
+  __pyx_ptype_5numpy_generic = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "generic", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_generic) __PYX_ERR(1, 812, __pyx_L1_error)
+  __pyx_ptype_5numpy_number = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "number", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_number) __PYX_ERR(1, 814, __pyx_L1_error)
+  __pyx_ptype_5numpy_integer = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "integer", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_integer) __PYX_ERR(1, 816, __pyx_L1_error)
+  __pyx_ptype_5numpy_signedinteger = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "signedinteger", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_signedinteger) __PYX_ERR(1, 818, __pyx_L1_error)
+  __pyx_ptype_5numpy_unsignedinteger = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "unsignedinteger", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_unsignedinteger) __PYX_ERR(1, 820, __pyx_L1_error)
+  __pyx_ptype_5numpy_inexact = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "inexact", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_inexact) __PYX_ERR(1, 822, __pyx_L1_error)
+  __pyx_ptype_5numpy_floating = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "floating", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_floating) __PYX_ERR(1, 824, __pyx_L1_error)
+  __pyx_ptype_5numpy_complexfloating = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "complexfloating", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_complexfloating) __PYX_ERR(1, 826, __pyx_L1_error)
+  __pyx_ptype_5numpy_flexible = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "flexible", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_flexible) __PYX_ERR(1, 828, __pyx_L1_error)
+  __pyx_ptype_5numpy_character = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "character", sizeof(PyObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyObject),__Pyx_ImportType_CheckSize_Warn_3_0_8); if (!__pyx_ptype_5numpy_character) __PYX_ERR(1, 830, __pyx_L1_error)
+  __pyx_ptype_5numpy_ufunc = __Pyx_ImportType_3_0_8(__pyx_t_1, "numpy", "ufunc", sizeof(PyUFuncObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_8(PyUFuncObject),__Pyx_ImportType_CheckSize_Ignore_3_0_8); if (!__pyx_ptype_5numpy_ufunc) __PYX_ERR(1, 868, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
   __Pyx_RefNannyFinishContext();
   return 0;
@@ -6408,6 +8260,7 @@ static CYTHON_SMALL_CODE int __pyx_pymod_exec__openjpeg(PyObject *__pyx_pyinit_m
   PyObject *__pyx_t_1 = NULL;
   PyObject *__pyx_t_2 = NULL;
   PyObject *__pyx_t_3 = NULL;
+  PyObject *__pyx_t_4 = NULL;
   int __pyx_lineno = 0;
   const char *__pyx_filename = NULL;
   int __pyx_clineno = 0;
@@ -6527,8 +8380,8 @@ if (!__Pyx_RefNanny) {
  * # cython: language_level=3
  * # distutils: language=c
  * from math import ceil             # <<<<<<<<<<<<<<
- * from typing import Union, Dict, BinaryIO
- * 
+ * from io import BytesIO
+ * import logging
  */
   __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 3, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
@@ -6547,155 +8400,240 @@ if (!__Pyx_RefNanny) {
   /* "_openjpeg.pyx":4
  * # distutils: language=c
  * from math import ceil
- * from typing import Union, Dict, BinaryIO             # <<<<<<<<<<<<<<
+ * from io import BytesIO             # <<<<<<<<<<<<<<
+ * import logging
+ * from typing import Union, Dict, BinaryIO, Tuple, List
+ */
+  __pyx_t_3 = PyList_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  __Pyx_INCREF(__pyx_n_s_BytesIO);
+  __Pyx_GIVEREF(__pyx_n_s_BytesIO);
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_n_s_BytesIO)) __PYX_ERR(0, 4, __pyx_L1_error);
+  __pyx_t_2 = __Pyx_Import(__pyx_n_s_io, __pyx_t_3, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 4, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  __pyx_t_3 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_BytesIO); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_BytesIO, __pyx_t_3) < 0) __PYX_ERR(0, 4, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":5
+ * from math import ceil
+ * from io import BytesIO
+ * import logging             # <<<<<<<<<<<<<<
+ * from typing import Union, Dict, BinaryIO, Tuple, List
+ * 
+ */
+  __pyx_t_2 = __Pyx_ImportDottedModule(__pyx_n_s_logging, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 5, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_logging, __pyx_t_2) < 0) __PYX_ERR(0, 5, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+  /* "_openjpeg.pyx":6
+ * from io import BytesIO
+ * import logging
+ * from typing import Union, Dict, BinaryIO, Tuple, List             # <<<<<<<<<<<<<<
  * 
  * from libc.stdint cimport uint32_t
  */
-  __pyx_t_3 = PyList_New(3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_3);
+  __pyx_t_2 = PyList_New(5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
   __Pyx_INCREF(__pyx_n_s_Union);
   __Pyx_GIVEREF(__pyx_n_s_Union);
-  if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_n_s_Union)) __PYX_ERR(0, 4, __pyx_L1_error);
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_n_s_Union)) __PYX_ERR(0, 6, __pyx_L1_error);
   __Pyx_INCREF(__pyx_n_s_Dict);
   __Pyx_GIVEREF(__pyx_n_s_Dict);
-  if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 1, __pyx_n_s_Dict)) __PYX_ERR(0, 4, __pyx_L1_error);
-  __Pyx_INCREF(__pyx_n_s_BinaryIO);
-  __Pyx_GIVEREF(__pyx_n_s_BinaryIO);
-  if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 2, __pyx_n_s_BinaryIO)) __PYX_ERR(0, 4, __pyx_L1_error);
-  __pyx_t_2 = __Pyx_Import(__pyx_n_s_typing, __pyx_t_3, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
-  __pyx_t_3 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_Union); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_3);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_Union, __pyx_t_3) < 0) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
-  __pyx_t_3 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_Dict); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_3);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_Dict, __pyx_t_3) < 0) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
-  __pyx_t_3 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_BinaryIO); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 1, __pyx_n_s_Dict)) __PYX_ERR(0, 6, __pyx_L1_error);
+  __Pyx_INCREF(__pyx_n_s_BinaryIO);
+  __Pyx_GIVEREF(__pyx_n_s_BinaryIO);
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 2, __pyx_n_s_BinaryIO)) __PYX_ERR(0, 6, __pyx_L1_error);
+  __Pyx_INCREF(__pyx_n_s_Tuple);
+  __Pyx_GIVEREF(__pyx_n_s_Tuple);
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 3, __pyx_n_s_Tuple)) __PYX_ERR(0, 6, __pyx_L1_error);
+  __Pyx_INCREF(__pyx_n_s_List);
+  __Pyx_GIVEREF(__pyx_n_s_List);
+  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 4, __pyx_n_s_List)) __PYX_ERR(0, 6, __pyx_L1_error);
+  __pyx_t_3 = __Pyx_Import(__pyx_n_s_typing, __pyx_t_2, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_3);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_BinaryIO, __pyx_t_3) < 0) __PYX_ERR(0, 4, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_Union); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_Union, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_Dict); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_Dict, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_BinaryIO); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_BinaryIO, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_Tuple); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_Tuple, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_List); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_2);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_List, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
 
-  /* "_openjpeg.pyx":9
+  /* "_openjpeg.pyx":11
  * 
  * from cpython.ref cimport PyObject
  * import numpy as np             # <<<<<<<<<<<<<<
- * cimport numpy as np
+ * cimport numpy as cnp
+ * 
+ */
+  __pyx_t_3 = __Pyx_ImportDottedModule(__pyx_n_s_numpy, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 11, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_3) < 0) __PYX_ERR(0, 11, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+  /* "_openjpeg.pyx":39
+ * 
  * 
+ * LOGGER = logging.getLogger(__name__)             # <<<<<<<<<<<<<<
+ * ERRORS = {
+ *     1: "failed to create the input stream",
  */
-  __pyx_t_2 = __Pyx_ImportDottedModule(__pyx_n_s_numpy, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 9, __pyx_L1_error)
+  __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_logging); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 39, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_getLogger); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 39, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_2);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_2) < 0) __PYX_ERR(0, 9, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_name); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 39, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  __pyx_t_4 = __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 39, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_LOGGER, __pyx_t_4) < 0) __PYX_ERR(0, 39, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
 
-  /* "_openjpeg.pyx":27
- * 
+  /* "_openjpeg.pyx":41
+ * LOGGER = logging.getLogger(__name__)
  * ERRORS = {
  *     1: "failed to create the input stream",             # <<<<<<<<<<<<<<
  *     2: "failed to setup the decoder",
  *     3: "failed to read the header",
  */
-  __pyx_t_2 = __Pyx_PyDict_NewPresized(8); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 27, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_1, __pyx_kp_u_failed_to_create_the_input_strea) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_2, __pyx_kp_u_failed_to_setup_the_decoder) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_3, __pyx_kp_u_failed_to_read_the_header) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_4, __pyx_kp_u_failed_to_set_the_component_indi) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_5, __pyx_kp_u_failed_to_set_the_decoded_area) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_6, __pyx_kp_u_failed_to_decode_image) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_7, __pyx_kp_u_support_for_more_than_16_bits_pe) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_int_8, __pyx_kp_u_failed_to_upscale_subsampled_com) < 0) __PYX_ERR(0, 27, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_ERRORS, __pyx_t_2) < 0) __PYX_ERR(0, 26, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_4 = __Pyx_PyDict_NewPresized(8); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 41, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_1, __pyx_kp_u_failed_to_create_the_input_strea) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_2, __pyx_kp_u_failed_to_setup_the_decoder) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_3, __pyx_kp_u_failed_to_read_the_header) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_4, __pyx_kp_u_failed_to_set_the_component_indi) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_5, __pyx_kp_u_failed_to_set_the_decoded_area) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_6, __pyx_kp_u_failed_to_decode_image) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_7, __pyx_kp_u_support_for_more_than_16_bits_pe) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_int_8, __pyx_kp_u_failed_to_upscale_subsampled_com) < 0) __PYX_ERR(0, 41, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_ERRORS, __pyx_t_4) < 0) __PYX_ERR(0, 40, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
 
-  /* "_openjpeg.pyx":38
+  /* "_openjpeg.pyx":52
  * 
  * 
  * def get_version() -> bytes:             # <<<<<<<<<<<<<<
  *     """Return the openjpeg version as bytes."""
  *     cdef char *version = OpenJpegVersion()
  */
-  __pyx_t_2 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 38, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_return, __pyx_n_s_bytes) < 0) __PYX_ERR(0, 38, __pyx_L1_error)
-  __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_1get_version, 0, __pyx_n_s_get_version, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__7)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 38, __pyx_L1_error)
+  __pyx_t_4 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 52, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_return, __pyx_n_s_bytes) < 0) __PYX_ERR(0, 52, __pyx_L1_error)
+  __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_1get_version, 0, __pyx_n_s_get_version, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__13)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 52, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_3);
-  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_3, __pyx_t_2);
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_get_version, __pyx_t_3) < 0) __PYX_ERR(0, 38, __pyx_L1_error)
+  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_3, __pyx_t_4);
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_get_version, __pyx_t_3) < 0) __PYX_ERR(0, 52, __pyx_L1_error)
   __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
 
-  /* "_openjpeg.pyx":45
+  /* "_openjpeg.pyx":59
  * 
  * 
  * def decode(             # <<<<<<<<<<<<<<
  *     fp: BinaryIO,
  *     codec: int = 0,
  */
-  __pyx_t_3 = __Pyx_PyDict_NewPresized(4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 45, __pyx_L1_error)
+  __pyx_t_3 = __Pyx_PyDict_NewPresized(4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 59, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_3);
-  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_fp, __pyx_n_s_BinaryIO) < 0) __PYX_ERR(0, 45, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_codec, __pyx_n_s_int) < 0) __PYX_ERR(0, 45, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_as_array, __pyx_n_s_bool) < 0) __PYX_ERR(0, 45, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_return, __pyx_kp_s_Union_np_ndarray_bytearray) < 0) __PYX_ERR(0, 45, __pyx_L1_error)
-  __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_3decode, 0, __pyx_n_s_decode, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__9)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_2);
-  if (!__Pyx_CyFunction_InitDefaults(__pyx_t_2, sizeof(__pyx_defaults), 1)) __PYX_ERR(0, 45, __pyx_L1_error)
-
-  /* "_openjpeg.pyx":47
+  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_fp, __pyx_n_s_BinaryIO) < 0) __PYX_ERR(0, 59, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_codec, __pyx_n_s_int) < 0) __PYX_ERR(0, 59, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_as_array, __pyx_n_s_bool) < 0) __PYX_ERR(0, 59, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_return, __pyx_kp_s_Union_np_ndarray_bytearray) < 0) __PYX_ERR(0, 59, __pyx_L1_error)
+  __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_3decode, 0, __pyx_n_s_decode, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__15)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 59, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  if (!__Pyx_CyFunction_InitDefaults(__pyx_t_4, sizeof(__pyx_defaults), 1)) __PYX_ERR(0, 59, __pyx_L1_error)
+
+  /* "_openjpeg.pyx":61
  * def decode(
  *     fp: BinaryIO,
  *     codec: int = 0,             # <<<<<<<<<<<<<<
  *     as_array: bool = False
  * ) -> Union[np.ndarray, bytearray]:
  */
-  if (!(likely(__Pyx_Py3Int_CheckExact(__pyx_int_0)) || __Pyx_RaiseUnexpectedTypeError("int", __pyx_int_0))) __PYX_ERR(0, 47, __pyx_L1_error)
+  if (!(likely(__Pyx_Py3Int_CheckExact(__pyx_int_0)) || __Pyx_RaiseUnexpectedTypeError("int", __pyx_int_0))) __PYX_ERR(0, 61, __pyx_L1_error)
   __Pyx_INCREF(__pyx_int_0);
-  __Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_t_2)->__pyx_arg_codec = ((PyObject*)__pyx_int_0);
+  __Pyx_CyFunction_Defaults(__pyx_defaults, __pyx_t_4)->__pyx_arg_codec = ((PyObject*)__pyx_int_0);
   __Pyx_GIVEREF(__pyx_int_0);
-  __Pyx_CyFunction_SetDefaultsGetter(__pyx_t_2, __pyx_pf_9_openjpeg_6__defaults__);
-  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_2, __pyx_t_3);
+  __Pyx_CyFunction_SetDefaultsGetter(__pyx_t_4, __pyx_pf_9_openjpeg_8__defaults__);
+  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_4, __pyx_t_3);
   __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_decode, __pyx_t_2) < 0) __PYX_ERR(0, 45, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_decode, __pyx_t_4) < 0) __PYX_ERR(0, 59, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
 
-  /* "_openjpeg.pyx":101
+  /* "_openjpeg.pyx":113
  * 
  * 
  * def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bool]]:             # <<<<<<<<<<<<<<
  *     """Return a :class:`dict` containing the JPEG 2000 image parameters.
  * 
  */
-  __pyx_t_2 = __Pyx_PyDict_NewPresized(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 101, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_fp, __pyx_n_s_BinaryIO) < 0) __PYX_ERR(0, 101, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_codec, __pyx_n_s_int) < 0) __PYX_ERR(0, 101, __pyx_L1_error)
-  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_return, __pyx_kp_s_Dict_str_Union_str_int_bool) < 0) __PYX_ERR(0, 101, __pyx_L1_error)
-  __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_5get_parameters, 0, __pyx_n_s_get_parameters, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__11)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 101, __pyx_L1_error)
+  __pyx_t_4 = __Pyx_PyDict_NewPresized(3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 113, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_fp, __pyx_n_s_BinaryIO) < 0) __PYX_ERR(0, 113, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_codec, __pyx_n_s_int) < 0) __PYX_ERR(0, 113, __pyx_L1_error)
+  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_return, __pyx_kp_s_Dict_str_Union_str_int_bool) < 0) __PYX_ERR(0, 113, __pyx_L1_error)
+  __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_5get_parameters, 0, __pyx_n_s_get_parameters, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__17)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 113, __pyx_L1_error)
   __Pyx_GOTREF(__pyx_t_3);
-  if (!__Pyx_CyFunction_InitDefaults(__pyx_t_3, sizeof(__pyx_defaults1), 1)) __PYX_ERR(0, 101, __pyx_L1_error)
-  if (!(likely(__Pyx_Py3Int_CheckExact(__pyx_int_0)) || __Pyx_RaiseUnexpectedTypeError("int", __pyx_int_0))) __PYX_ERR(0, 101, __pyx_L1_error)
+  if (!__Pyx_CyFunction_InitDefaults(__pyx_t_3, sizeof(__pyx_defaults1), 1)) __PYX_ERR(0, 113, __pyx_L1_error)
+  if (!(likely(__Pyx_Py3Int_CheckExact(__pyx_int_0)) || __Pyx_RaiseUnexpectedTypeError("int", __pyx_int_0))) __PYX_ERR(0, 113, __pyx_L1_error)
   __Pyx_INCREF(__pyx_int_0);
   __Pyx_CyFunction_Defaults(__pyx_defaults1, __pyx_t_3)->__pyx_arg_codec = ((PyObject*)__pyx_int_0);
   __Pyx_GIVEREF(__pyx_int_0);
-  __Pyx_CyFunction_SetDefaultsGetter(__pyx_t_3, __pyx_pf_9_openjpeg_8__defaults__);
-  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_3, __pyx_t_2);
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_get_parameters, __pyx_t_3) < 0) __PYX_ERR(0, 101, __pyx_L1_error)
+  __Pyx_CyFunction_SetDefaultsGetter(__pyx_t_3, __pyx_pf_9_openjpeg_10__defaults__);
+  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_3, __pyx_t_4);
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_get_parameters, __pyx_t_3) < 0) __PYX_ERR(0, 113, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+  /* "_openjpeg.pyx":195
+ * 
+ * 
+ * def encode(             # <<<<<<<<<<<<<<
+ *     cnp.ndarray arr,
+ *     int bits_stored,
+ */
+  __pyx_t_3 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 195, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_3);
+  if (PyDict_SetItem(__pyx_t_3, __pyx_n_s_return, __pyx_kp_s_Tuple_int_bytes) < 0) __PYX_ERR(0, 195, __pyx_L1_error)
+  __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9_openjpeg_7encode, 0, __pyx_n_s_encode, NULL, __pyx_n_s_openjpeg, __pyx_d, ((PyObject *)__pyx_codeobj__19)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 195, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_4, __pyx_t_3);
   __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_encode, __pyx_t_4) < 0) __PYX_ERR(0, 195, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
 
   /* "_openjpeg.pyx":1
  * # cython: language_level=3             # <<<<<<<<<<<<<<
  * # distutils: language=c
  * from math import ceil
  */
-  __pyx_t_3 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_3);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_3) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  __pyx_t_4 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_4);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_4) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
 
   /*--- Wrapped vars code ---*/
 
@@ -6703,6 +8641,7 @@ if (!__Pyx_RefNanny) {
   __pyx_L1_error:;
   __Pyx_XDECREF(__pyx_t_2);
   __Pyx_XDECREF(__pyx_t_3);
+  __Pyx_XDECREF(__pyx_t_4);
   if (__pyx_m) {
     if (__pyx_d && stringtab_initialized) {
       __Pyx_AddTraceback("init _openjpeg", __pyx_clineno, __pyx_lineno, __pyx_filename);
@@ -7487,11 +9426,11 @@ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyO
     {
         int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ);
         if (unlikely(eq != 0)) {
-            if (unlikely(eq < 0)) return NULL;  // error
+            if (unlikely(eq < 0)) return NULL;
             return kwvalues[i];
         }
     }
-    return NULL;  // not found (no exception set)
+    return NULL;
 }
 #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
 CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) {
@@ -7578,7 +9517,7 @@ static int __Pyx_ParseOptionalKeywords(
         if (*name) {
             values[name-argnames] = value;
 #if CYTHON_AVOID_BORROWED_REFS
-            Py_INCREF(value); // transfer ownership of value to values
+            Py_INCREF(value);
             Py_DECREF(key);
 #endif
             key = NULL;
@@ -7597,7 +9536,7 @@ static int __Pyx_ParseOptionalKeywords(
                         && _PyString_Eq(**name, key)) {
                     values[name-argnames] = value;
 #if CYTHON_AVOID_BORROWED_REFS
-                    value = NULL;  // ownership transferred to values
+                    value = NULL;
 #endif
                     break;
                 }
@@ -7629,7 +9568,7 @@ static int __Pyx_ParseOptionalKeywords(
                 if (cmp == 0) {
                     values[name-argnames] = value;
 #if CYTHON_AVOID_BORROWED_REFS
-                    value = NULL; // ownership transferred to values
+                    value = NULL;
 #endif
                     break;
                 }
@@ -8150,6 +10089,78 @@ static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key) {
 }
 #endif
 
+/* PyIntCompare */
+static CYTHON_INLINE int __Pyx_PyInt_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace) {
+    CYTHON_MAYBE_UNUSED_VAR(intval);
+    CYTHON_UNUSED_VAR(inplace);
+    if (op1 == op2) {
+        return 1;
+    }
+    #if PY_MAJOR_VERSION < 3
+    if (likely(PyInt_CheckExact(op1))) {
+        const long b = intval;
+        long a = PyInt_AS_LONG(op1);
+        return (a == b);
+    }
+    #endif
+    #if CYTHON_USE_PYLONG_INTERNALS
+    if (likely(PyLong_CheckExact(op1))) {
+        int unequal;
+        unsigned long uintval;
+        Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+        const digit* digits = __Pyx_PyLong_Digits(op1);
+        if (intval == 0) {
+            return (__Pyx_PyLong_IsZero(op1) == 1);
+        } else if (intval < 0) {
+            if (__Pyx_PyLong_IsNonNeg(op1))
+                return 0;
+            intval = -intval;
+        } else {
+            if (__Pyx_PyLong_IsNeg(op1))
+                return 0;
+        }
+        uintval = (unsigned long) intval;
+#if PyLong_SHIFT * 4 < SIZEOF_LONG*8
+        if (uintval >> (PyLong_SHIFT * 4)) {
+            unequal = (size != 5) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+                 | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[4] != ((uintval >> (4 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+        } else
+#endif
+#if PyLong_SHIFT * 3 < SIZEOF_LONG*8
+        if (uintval >> (PyLong_SHIFT * 3)) {
+            unequal = (size != 4) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+                 | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+        } else
+#endif
+#if PyLong_SHIFT * 2 < SIZEOF_LONG*8
+        if (uintval >> (PyLong_SHIFT * 2)) {
+            unequal = (size != 3) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+                 | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+        } else
+#endif
+#if PyLong_SHIFT * 1 < SIZEOF_LONG*8
+        if (uintval >> (PyLong_SHIFT * 1)) {
+            unequal = (size != 2) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+                 | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+        } else
+#endif
+            unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK));
+        return (unequal == 0);
+    }
+    #endif
+    if (PyFloat_CheckExact(op1)) {
+        const long b = intval;
+#if CYTHON_COMPILING_IN_LIMITED_API
+        double a = __pyx_PyFloat_AsDouble(op1);
+#else
+        double a = PyFloat_AS_DOUBLE(op1);
+#endif
+        return ((double)a == (double)b);
+    }
+    return __Pyx_PyObject_IsTrueAndDecref(
+        PyObject_RichCompare(op1, op2, Py_EQ));
+}
+
 /* ExtTypeTest */
 static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
     __Pyx_TypeName obj_type_name;
@@ -8276,11 +10287,551 @@ static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
     PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
 }
 
+/* PyIntBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_PyInt_MultiplyObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+    CYTHON_MAYBE_UNUSED_VAR(intval);
+    CYTHON_MAYBE_UNUSED_VAR(inplace);
+    CYTHON_UNUSED_VAR(zerodivision_check);
+    #if PY_MAJOR_VERSION < 3
+    if (likely(PyInt_CheckExact(op1))) {
+        const long b = intval;
+        long a = PyInt_AS_LONG(op1);
+        
+#ifdef HAVE_LONG_LONG
+            if (sizeof(PY_LONG_LONG) > sizeof(long)) {
+                PY_LONG_LONG result = (PY_LONG_LONG)a * (PY_LONG_LONG)b;
+                return (result >= LONG_MIN && result <= LONG_MAX) ?
+                    PyInt_FromLong((long)result) : PyLong_FromLongLong(result);
+            }
+#endif
+#if CYTHON_USE_TYPE_SLOTS
+            return PyInt_Type.tp_as_number->nb_multiply(op1, op2);
+#else
+            return PyNumber_Multiply(op1, op2);
+#endif
+    }
+    #endif
+    #if CYTHON_USE_PYLONG_INTERNALS
+    if (likely(PyLong_CheckExact(op1))) {
+        const long b = intval;
+        long a, x;
+#ifdef HAVE_LONG_LONG
+        const PY_LONG_LONG llb = intval;
+        PY_LONG_LONG lla, llx;
+#endif
+        if (unlikely(__Pyx_PyLong_IsZero(op1))) {
+            return __Pyx_NewRef(op1);
+        }
+        if (likely(__Pyx_PyLong_IsCompact(op1))) {
+            a = __Pyx_PyLong_CompactValue(op1);
+        } else {
+            const digit* digits = __Pyx_PyLong_Digits(op1);
+            const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(op1);
+            switch (size) {
+                case -2:
+                    if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT+30) {
+                        a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT+30) {
+                        lla = -(PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                case 2:
+                    if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT+30) {
+                        a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT+30) {
+                        lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                case -3:
+                    if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT+30) {
+                        a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT+30) {
+                        lla = -(PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                case 3:
+                    if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT+30) {
+                        a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT+30) {
+                        lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                case -4:
+                    if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT+30) {
+                        a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT+30) {
+                        lla = -(PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                case 4:
+                    if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT+30) {
+                        a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+                        break;
+                    #ifdef HAVE_LONG_LONG
+                    } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT+30) {
+                        lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+                        goto long_long;
+                    #endif
+                    }
+                    CYTHON_FALLTHROUGH;
+                default: return PyLong_Type.tp_as_number->nb_multiply(op1, op2);
+            }
+        }
+                CYTHON_UNUSED_VAR(a);
+                CYTHON_UNUSED_VAR(b);
+                #ifdef HAVE_LONG_LONG
+                lla = a;
+                goto long_long;
+                #else
+                return PyLong_Type.tp_as_number->nb_multiply(op1, op2);
+                #endif
+            return PyLong_FromLong(x);
+#ifdef HAVE_LONG_LONG
+        long_long:
+                llx = lla * llb;
+            return PyLong_FromLongLong(llx);
+#endif
+        
+        
+    }
+    #endif
+    if (PyFloat_CheckExact(op1)) {
+        const long b = intval;
+#if CYTHON_COMPILING_IN_LIMITED_API
+        double a = __pyx_PyFloat_AsDouble(op1);
+#else
+        double a = PyFloat_AS_DOUBLE(op1);
+#endif
+            double result;
+            
+            PyFPE_START_PROTECT("multiply", return NULL)
+            result = ((double)a) * (double)b;
+            PyFPE_END_PROTECT(result)
+            return PyFloat_FromDouble(result);
+    }
+    return (inplace ? PyNumber_InPlaceMultiply : PyNumber_Multiply)(op1, op2);
+}
+#endif
+
+/* JoinPyUnicode */
+static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_count, Py_ssize_t result_ulength,
+                                      Py_UCS4 max_char) {
+#if CYTHON_USE_UNICODE_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+    PyObject *result_uval;
+    int result_ukind, kind_shift;
+    Py_ssize_t i, char_pos;
+    void *result_udata;
+    CYTHON_MAYBE_UNUSED_VAR(max_char);
+#if CYTHON_PEP393_ENABLED
+    result_uval = PyUnicode_New(result_ulength, max_char);
+    if (unlikely(!result_uval)) return NULL;
+    result_ukind = (max_char <= 255) ? PyUnicode_1BYTE_KIND : (max_char <= 65535) ? PyUnicode_2BYTE_KIND : PyUnicode_4BYTE_KIND;
+    kind_shift = (result_ukind == PyUnicode_4BYTE_KIND) ? 2 : result_ukind - 1;
+    result_udata = PyUnicode_DATA(result_uval);
+#else
+    result_uval = PyUnicode_FromUnicode(NULL, result_ulength);
+    if (unlikely(!result_uval)) return NULL;
+    result_ukind = sizeof(Py_UNICODE);
+    kind_shift = (result_ukind == 4) ? 2 : result_ukind - 1;
+    result_udata = PyUnicode_AS_UNICODE(result_uval);
+#endif
+    assert(kind_shift == 2 || kind_shift == 1 || kind_shift == 0);
+    char_pos = 0;
+    for (i=0; i < value_count; i++) {
+        int ukind;
+        Py_ssize_t ulength;
+        void *udata;
+        PyObject *uval = PyTuple_GET_ITEM(value_tuple, i);
+        if (unlikely(__Pyx_PyUnicode_READY(uval)))
+            goto bad;
+        ulength = __Pyx_PyUnicode_GET_LENGTH(uval);
+        if (unlikely(!ulength))
+            continue;
+        if (unlikely((PY_SSIZE_T_MAX >> kind_shift) - ulength < char_pos))
+            goto overflow;
+        ukind = __Pyx_PyUnicode_KIND(uval);
+        udata = __Pyx_PyUnicode_DATA(uval);
+        if (!CYTHON_PEP393_ENABLED || ukind == result_ukind) {
+            memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift));
+        } else {
+            #if PY_VERSION_HEX >= 0x030d0000
+            if (unlikely(PyUnicode_CopyCharacters(result_uval, char_pos, uval, 0, ulength) < 0)) goto bad;
+            #elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030300F0 || defined(_PyUnicode_FastCopyCharacters)
+            _PyUnicode_FastCopyCharacters(result_uval, char_pos, uval, 0, ulength);
+            #else
+            Py_ssize_t j;
+            for (j=0; j < ulength; j++) {
+                Py_UCS4 uchar = __Pyx_PyUnicode_READ(ukind, udata, j);
+                __Pyx_PyUnicode_WRITE(result_ukind, result_udata, char_pos+j, uchar);
+            }
+            #endif
+        }
+        char_pos += ulength;
+    }
+    return result_uval;
+overflow:
+    PyErr_SetString(PyExc_OverflowError, "join() result is too long for a Python string");
+bad:
+    Py_DECREF(result_uval);
+    return NULL;
+#else
+    CYTHON_UNUSED_VAR(max_char);
+    CYTHON_UNUSED_VAR(result_ulength);
+    CYTHON_UNUSED_VAR(value_count);
+    return PyUnicode_Join(__pyx_empty_unicode, value_tuple);
+#endif
+}
+
+/* CIntToDigits */
+static const char DIGIT_PAIRS_10[2*10*10+1] = {
+    "00010203040506070809"
+    "10111213141516171819"
+    "20212223242526272829"
+    "30313233343536373839"
+    "40414243444546474849"
+    "50515253545556575859"
+    "60616263646566676869"
+    "70717273747576777879"
+    "80818283848586878889"
+    "90919293949596979899"
+};
+static const char DIGIT_PAIRS_8[2*8*8+1] = {
+    "0001020304050607"
+    "1011121314151617"
+    "2021222324252627"
+    "3031323334353637"
+    "4041424344454647"
+    "5051525354555657"
+    "6061626364656667"
+    "7071727374757677"
+};
+static const char DIGITS_HEX[2*16+1] = {
+    "0123456789abcdef"
+    "0123456789ABCDEF"
+};
+
+/* BuildPyUnicode */
+static PyObject* __Pyx_PyUnicode_BuildFromAscii(Py_ssize_t ulength, char* chars, int clength,
+                                                int prepend_sign, char padding_char) {
+    PyObject *uval;
+    Py_ssize_t uoffset = ulength - clength;
+#if CYTHON_USE_UNICODE_INTERNALS
+    Py_ssize_t i;
+#if CYTHON_PEP393_ENABLED
+    void *udata;
+    uval = PyUnicode_New(ulength, 127);
+    if (unlikely(!uval)) return NULL;
+    udata = PyUnicode_DATA(uval);
+#else
+    Py_UNICODE *udata;
+    uval = PyUnicode_FromUnicode(NULL, ulength);
+    if (unlikely(!uval)) return NULL;
+    udata = PyUnicode_AS_UNICODE(uval);
+#endif
+    if (uoffset > 0) {
+        i = 0;
+        if (prepend_sign) {
+            __Pyx_PyUnicode_WRITE(PyUnicode_1BYTE_KIND, udata, 0, '-');
+            i++;
+        }
+        for (; i < uoffset; i++) {
+            __Pyx_PyUnicode_WRITE(PyUnicode_1BYTE_KIND, udata, i, padding_char);
+        }
+    }
+    for (i=0; i < clength; i++) {
+        __Pyx_PyUnicode_WRITE(PyUnicode_1BYTE_KIND, udata, uoffset+i, chars[i]);
+    }
+#else
+    {
+        PyObject *sign = NULL, *padding = NULL;
+        uval = NULL;
+        if (uoffset > 0) {
+            prepend_sign = !!prepend_sign;
+            if (uoffset > prepend_sign) {
+                padding = PyUnicode_FromOrdinal(padding_char);
+                if (likely(padding) && uoffset > prepend_sign + 1) {
+                    PyObject *tmp;
+                    PyObject *repeat = PyInt_FromSsize_t(uoffset - prepend_sign);
+                    if (unlikely(!repeat)) goto done_or_error;
+                    tmp = PyNumber_Multiply(padding, repeat);
+                    Py_DECREF(repeat);
+                    Py_DECREF(padding);
+                    padding = tmp;
+                }
+                if (unlikely(!padding)) goto done_or_error;
+            }
+            if (prepend_sign) {
+                sign = PyUnicode_FromOrdinal('-');
+                if (unlikely(!sign)) goto done_or_error;
+            }
+        }
+        uval = PyUnicode_DecodeASCII(chars, clength, NULL);
+        if (likely(uval) && padding) {
+            PyObject *tmp = PyNumber_Add(padding, uval);
+            Py_DECREF(uval);
+            uval = tmp;
+        }
+        if (likely(uval) && sign) {
+            PyObject *tmp = PyNumber_Add(sign, uval);
+            Py_DECREF(uval);
+            uval = tmp;
+        }
+done_or_error:
+        Py_XDECREF(padding);
+        Py_XDECREF(sign);
+    }
+#endif
+    return uval;
+}
+
+/* CIntToPyUnicode */
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_From_int(int value, Py_ssize_t width, char padding_char, char format_char) {
+    char digits[sizeof(int)*3+2];
+    char *dpos, *end = digits + sizeof(int)*3+2;
+    const char *hex_digits = DIGITS_HEX;
+    Py_ssize_t length, ulength;
+    int prepend_sign, last_one_off;
+    int remaining;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+    const int neg_one = (int) -1, const_zero = (int) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+    const int is_unsigned = neg_one > const_zero;
+    if (format_char == 'X') {
+        hex_digits += 16;
+        format_char = 'x';
+    }
+    remaining = value;
+    last_one_off = 0;
+    dpos = end;
+    do {
+        int digit_pos;
+        switch (format_char) {
+        case 'o':
+            digit_pos = abs((int)(remaining % (8*8)));
+            remaining = (int) (remaining / (8*8));
+            dpos -= 2;
+            memcpy(dpos, DIGIT_PAIRS_8 + digit_pos * 2, 2);
+            last_one_off = (digit_pos < 8);
+            break;
+        case 'd':
+            digit_pos = abs((int)(remaining % (10*10)));
+            remaining = (int) (remaining / (10*10));
+            dpos -= 2;
+            memcpy(dpos, DIGIT_PAIRS_10 + digit_pos * 2, 2);
+            last_one_off = (digit_pos < 10);
+            break;
+        case 'x':
+            *(--dpos) = hex_digits[abs((int)(remaining % 16))];
+            remaining = (int) (remaining / 16);
+            break;
+        default:
+            assert(0);
+            break;
+        }
+    } while (unlikely(remaining != 0));
+    assert(!last_one_off || *dpos == '0');
+    dpos += last_one_off;
+    length = end - dpos;
+    ulength = length;
+    prepend_sign = 0;
+    if (!is_unsigned && value <= neg_one) {
+        if (padding_char == ' ' || width <= length + 1) {
+            *(--dpos) = '-';
+            ++length;
+        } else {
+            prepend_sign = 1;
+        }
+        ++ulength;
+    }
+    if (width > ulength) {
+        ulength = width;
+    }
+    if (ulength == 1) {
+        return PyUnicode_FromOrdinal(*dpos);
+    }
+    return __Pyx_PyUnicode_BuildFromAscii(ulength, dpos, (int) length, prepend_sign, padding_char);
+}
+
+/* pybytes_as_double */
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj) {
+    PyObject *float_value;
+#if PY_MAJOR_VERSION >= 3
+    float_value = PyFloat_FromString(obj);
+#else
+    float_value = PyFloat_FromString(obj, 0);
+#endif
+    if (likely(float_value)) {
+#if CYTHON_ASSUME_SAFE_MACROS
+        double value = PyFloat_AS_DOUBLE(float_value);
+#else
+        double value = PyFloat_AsDouble(float_value);
+#endif
+        Py_DECREF(float_value);
+        return value;
+    }
+    return (double)-1;
+}
+static const char* __Pyx__PyBytes_AsDouble_Copy(const char* start, char* buffer, Py_ssize_t length) {
+    int last_was_punctuation = 1;
+    Py_ssize_t i;
+    for (i=0; i < length; i++) {
+        char chr = start[i];
+        int is_punctuation = (chr == '_') | (chr == '.') | (chr == 'e') | (chr == 'E');
+        *buffer = chr;
+        buffer += (chr != '_');
+        if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure;
+        last_was_punctuation = is_punctuation;
+    }
+    if (unlikely(last_was_punctuation)) goto parse_failure;
+    *buffer = '\0';
+    return buffer;
+parse_failure:
+    return NULL;
+}
+static double __Pyx__PyBytes_AsDouble_inf_nan(const char* start, Py_ssize_t length) {
+    int matches = 1;
+    char sign = start[0];
+    int is_signed = (sign == '+') | (sign == '-');
+    start += is_signed;
+    length -= is_signed;
+    switch (start[0]) {
+        #ifdef Py_NAN
+        case 'n':
+        case 'N':
+            if (unlikely(length != 3)) goto parse_failure;
+            matches &= (start[1] == 'a' || start[1] == 'A');
+            matches &= (start[2] == 'n' || start[2] == 'N');
+            if (unlikely(!matches)) goto parse_failure;
+            return (sign == '-') ? -Py_NAN : Py_NAN;
+        #endif
+        case 'i':
+        case 'I':
+            if (unlikely(length < 3)) goto parse_failure;
+            matches &= (start[1] == 'n' || start[1] == 'N');
+            matches &= (start[2] == 'f' || start[2] == 'F');
+            if (likely(length == 3 && matches))
+                return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+            if (unlikely(length != 8)) goto parse_failure;
+            matches &= (start[3] == 'i' || start[3] == 'I');
+            matches &= (start[4] == 'n' || start[4] == 'N');
+            matches &= (start[5] == 'i' || start[5] == 'I');
+            matches &= (start[6] == 't' || start[6] == 'T');
+            matches &= (start[7] == 'y' || start[7] == 'Y');
+            if (unlikely(!matches)) goto parse_failure;
+            return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+        case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+            break;
+        default:
+            goto parse_failure;
+    }
+    return 0.0;
+parse_failure:
+    return -1.0;
+}
+static CYTHON_INLINE int __Pyx__PyBytes_AsDouble_IsSpace(char ch) {
+    return (ch == 0x20) | !((ch < 0x9) | (ch > 0xd));
+}
+CYTHON_UNUSED static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) {
+    double value;
+    Py_ssize_t i, digits;
+    const char *last = start + length;
+    char *end;
+    while (__Pyx__PyBytes_AsDouble_IsSpace(*start))
+        start++;
+    while (start < last - 1 && __Pyx__PyBytes_AsDouble_IsSpace(last[-1]))
+        last--;
+    length = last - start;
+    if (unlikely(length <= 0)) goto fallback;
+    value = __Pyx__PyBytes_AsDouble_inf_nan(start, length);
+    if (unlikely(value == -1.0)) goto fallback;
+    if (value != 0.0) return value;
+    digits = 0;
+    for (i=0; i < length; digits += start[i++] != '_');
+    if (likely(digits == length)) {
+        value = PyOS_string_to_double(start, &end, NULL);
+    } else if (digits < 40) {
+        char number[40];
+        last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+        if (unlikely(!last)) goto fallback;
+        value = PyOS_string_to_double(number, &end, NULL);
+    } else {
+        char *number = (char*) PyMem_Malloc((digits + 1) * sizeof(char));
+        if (unlikely(!number)) goto fallback;
+        last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+        if (unlikely(!last)) {
+            PyMem_Free(number);
+            goto fallback;
+        }
+        value = PyOS_string_to_double(number, &end, NULL);
+        PyMem_Free(number);
+    }
+    if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+        return value;
+    }
+fallback:
+    return __Pyx_SlowPyString_AsDouble(obj);
+}
+
+/* pynumber_float */
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj) {
+    double val;
+    if (PyLong_CheckExact(obj)) {
+#if CYTHON_USE_PYLONG_INTERNALS
+        if (likely(__Pyx_PyLong_IsCompact(obj))) {
+            val = (double) __Pyx_PyLong_CompactValue(obj);
+            goto no_error;
+        }
+#endif
+        val = PyLong_AsDouble(obj);
+    } else if (PyUnicode_CheckExact(obj)) {
+        val = __Pyx_PyUnicode_AsDouble(obj);
+    } else if (PyBytes_CheckExact(obj)) {
+        val = __Pyx_PyBytes_AsDouble(obj);
+    } else if (PyByteArray_CheckExact(obj)) {
+        val = __Pyx_PyByteArray_AsDouble(obj);
+    } else {
+        return PyNumber_Float(obj);
+    }
+    if (unlikely(val == -1 && PyErr_Occurred())) {
+        return NULL;
+    }
+#if CYTHON_USE_PYLONG_INTERNALS
+no_error:
+#endif
+    return PyFloat_FromDouble(val);
+}
+
 /* TypeImport */
-#ifndef __PYX_HAVE_RT_ImportType_3_0_7
-#define __PYX_HAVE_RT_ImportType_3_0_7
-static PyTypeObject *__Pyx_ImportType_3_0_7(PyObject *module, const char *module_name, const char *class_name,
-    size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_7 check_size)
+#ifndef __PYX_HAVE_RT_ImportType_3_0_8
+#define __PYX_HAVE_RT_ImportType_3_0_8
+static PyTypeObject *__Pyx_ImportType_3_0_8(PyObject *module, const char *module_name, const char *class_name,
+    size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_8 check_size)
 {
     PyObject *result = 0;
     char warning[200];
@@ -8334,7 +10885,7 @@ static PyTypeObject *__Pyx_ImportType_3_0_7(PyObject *module, const char *module
             module_name, class_name, size, basicsize+itemsize);
         goto bad;
     }
-    if (check_size == __Pyx_ImportType_CheckSize_Error_3_0_7 &&
+    if (check_size == __Pyx_ImportType_CheckSize_Error_3_0_8 &&
             ((size_t)basicsize > size || (size_t)(basicsize + itemsize) < size)) {
         PyErr_Format(PyExc_ValueError,
             "%.200s.%.200s size changed, may indicate binary incompatibility. "
@@ -8342,7 +10893,7 @@ static PyTypeObject *__Pyx_ImportType_3_0_7(PyObject *module, const char *module
             module_name, class_name, size, basicsize, basicsize+itemsize);
         goto bad;
     }
-    else if (check_size == __Pyx_ImportType_CheckSize_Warn_3_0_7 && (size_t)basicsize > size) {
+    else if (check_size == __Pyx_ImportType_CheckSize_Warn_3_0_8 && (size_t)basicsize > size) {
         PyOS_snprintf(warning, sizeof(warning),
             "%s.%s size changed, may indicate binary incompatibility. "
             "Expected %zd from C header, got %zd from PyObject",
@@ -8427,7 +10978,7 @@ static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) {
         if (unlikely(!module_name_str)) { goto modbad; }
         module_name = PyUnicode_FromString(module_name_str);
         if (unlikely(!module_name)) { goto modbad; }
-        module_dot = PyUnicode_Concat(module_name, __pyx_kp_u__4);
+        module_dot = PyUnicode_Concat(module_name, __pyx_kp_u__10);
         if (unlikely(!module_dot)) { goto modbad; }
         full_name = PyUnicode_Concat(module_dot, name);
         if (unlikely(!full_name)) { goto modbad; }
@@ -8535,7 +11086,7 @@ static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *
 #endif
 static PyObject *__Pyx__ImportDottedModule(PyObject *name, PyObject *parts_tuple) {
 #if PY_MAJOR_VERSION < 3
-    PyObject *module, *from_list, *star = __pyx_n_s__5;
+    PyObject *module, *from_list, *star = __pyx_n_s__11;
     CYTHON_UNUSED_VAR(parts_tuple);
     from_list = PyList_New(1);
     if (unlikely(!from_list))
@@ -10114,7 +12665,7 @@ static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
     #else
     py_code = PyCode_NewEmpty(filename, funcname, py_line);
     #endif
-    Py_XDECREF(py_funcname);  // XDECREF since it's only set on Py3 if cline
+    Py_XDECREF(py_funcname);
     return py_code;
 bad:
     Py_XDECREF(py_funcname);
@@ -10968,7 +13519,7 @@ __Pyx_PyType_GetName(PyTypeObject* tp)
     if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) {
         PyErr_Clear();
         Py_XDECREF(name);
-        name = __Pyx_NewRef(__pyx_n_s__12);
+        name = __Pyx_NewRef(__pyx_n_s__20);
     }
     return name;
 }
diff --git a/openjpeg/_openjpeg.pyx b/openjpeg/_openjpeg.pyx
index 42fd041..64974b4 100644
--- a/openjpeg/_openjpeg.pyx
+++ b/openjpeg/_openjpeg.pyx
@@ -1,13 +1,15 @@
 # cython: language_level=3
 # distutils: language=c
 from math import ceil
-from typing import Union, Dict, BinaryIO
+from io import BytesIO
+import logging
+from typing import Union, Dict, BinaryIO, Tuple, List
 
 from libc.stdint cimport uint32_t
 
 from cpython.ref cimport PyObject
 import numpy as np
-cimport numpy as np
+cimport numpy as cnp
 
 cdef extern struct JPEG2000Parameters:
     uint32_t columns
@@ -21,8 +23,20 @@ cdef extern struct JPEG2000Parameters:
 cdef extern char* OpenJpegVersion()
 cdef extern int Decode(void* fp, unsigned char* out, int codec)
 cdef extern int GetParameters(void* fp, int codec, JPEG2000Parameters *param)
-
-
+cdef extern int Encode(
+    cnp.PyArrayObject* arr,
+    PyObject* dst,
+    int bits_stored,
+    int photometric_interpretation,
+    bint use_mct,
+    # bint lossless,
+    PyObject* compression_ratios,
+    PyObject* signal_noise_ratios,
+    int codec_format,
+)
+
+
+LOGGER = logging.getLogger(__name__)
 ERRORS = {
     1: "failed to create the input stream",
     2: "failed to setup the decoder",
@@ -78,24 +92,22 @@ def decode(
     """
     param = get_parameters(fp, codec)
     bpp = ceil(param['precision'] / 8)
+    if bpp == 3:
+        bpp = 4
     nr_bytes = param['rows'] * param['columns'] * param['nr_components'] * bpp
 
     cdef PyObject* p_in = <PyObject*>fp
     cdef unsigned char *p_out
     if as_array:
         out = np.zeros(nr_bytes, dtype=np.uint8)
-        p_out = <unsigned char *>np.PyArray_DATA(out)
+        p_out = <unsigned char *>cnp.PyArray_DATA(out)
     else:
         out = bytearray(nr_bytes)
         p_out = <unsigned char *>out
 
-    result = Decode(p_in, p_out, codec)
-    if result != 0:
-        raise RuntimeError(
-            f"Error decoding the J2K data: {ERRORS.get(result, result)}"
-        )
+    return_code = Decode(p_in, p_out, codec)
 
-    return out
+    return return_code, out
 
 
 def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bool]]:
@@ -178,3 +190,121 @@ def get_parameters(fp: BinaryIO, codec: int = 0) -> Dict[str, Union[str, int, bo
     }
 
     return parameters
+
+
+def encode(
+    cnp.ndarray arr,
+    int bits_stored,
+    int photometric_interpretation,
+    bint use_mct,
+    List[float] compression_ratios,
+    List[float] signal_noise_ratios,
+    int codec_format,
+) -> Tuple[int, bytes]:
+    """Return the JPEG 2000 compressed `arr`.
+
+    Parameters
+    ----------
+    arr : numpy.ndarray
+        The array containing the image data to be encoded.
+    bits_stored : int, optional
+        The number of bits used per pixel.
+    photometric_interpretation : int, optional
+        The colour space of the unencoded image data that will be set in the
+        JPEG 2000 metadata.
+    use_mct : bool, optional
+        If ``True`` then apply multi-component transformation (MCT) to RGB
+        images.
+    lossless : bool, optional
+        If ``True`` then use lossless encoding, otherwise use lossy encoding.
+    compression_ratios : list[float], optional
+        Required if using lossy encoding, this is the compression ratio to use
+        for each layer. Should be in decreasing order (such as ``[80, 30, 10]``)
+        and the final value may be ``1`` to indicate lossless encoding should
+        be used for that layer.
+    codec_format : int, optional
+        The codec to used when encoding:
+
+        * ``0``: JPEG 2000 codestream only (default) (J2K/J2C format)
+        * ``2``: A boxed JPEG 2000 codestream (JP2 format)
+
+    Returns
+    -------
+    int
+        The return code of the encoding, will be ``0`` for success, otherwise
+        encoding failed.
+    """
+    if not (1 <= bits_stored <= arr.dtype.itemsize * 8):
+        raise ValueError(
+            "Invalid value for the 'bits_stored' parameter, the value must be "
+            f"in the range (1, {arr.dtype.itemsize * 8})"
+        )
+
+    allowed = (bool, np.uint8, np.int8, np.uint16, np.int16, np.uint32, np.int32)
+    if arr.dtype not in allowed:
+        raise ValueError(
+            f"The input array has an unsupported dtype '{arr.dtype}', only bool, "
+            "u1, u2, u4, i1, i2 and i4 are supported"
+        )
+
+    # It seems like OpenJPEG can only encode up to 24 bits, although theoretically
+    #   based on their use of OPJ_INT32 for pixel values, it should be 32-bit for
+    #   signed and 31 bit for unsigned. Maybe I've made a mistake somewhere?
+    arr_max = arr.max()
+    arr_min = arr.min()
+    if (
+        (arr.dtype == np.uint32 and arr_max > 2**24 - 1)
+        or (arr.dtype == np.int32 and (arr_max > 2**23 - 1 or arr_min < -2**23))
+    ):
+        raise ValueError(
+            "The input array contains values outside the range of the maximum "
+            "supported bit-depth of 24"
+        )
+
+    # Check the array matches bits_stored
+    if arr.dtype in (np.uint8, np.uint16, np.uint32) and arr_max > 2**bits_stored - 1:
+        raise ValueError(
+            f"A 'bits_stored' value of {bits_stored} is incompatible with "
+            f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+        )
+
+    if (
+        arr.dtype in (np.int8, np.int16, np.int32)
+        and (arr_max > 2**(bits_stored - 1) - 1 or arr_min < -2**(bits_stored - 1))
+    ):
+        raise ValueError(
+            f"A 'bits_stored' value of {bits_stored} is incompatible with "
+            f"the range of pixel data in the input array: ({arr_min}, {arr_max})"
+        )
+
+    # MCT may be used with RGB in both lossy and lossless modes
+    use_mct = 1 if use_mct else 0
+
+    if codec_format not in (0, 2):
+        raise ValueError(
+            "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+        )
+
+    compression_ratios = [float(x) for x in compression_ratios]
+    signal_noise_ratios = [float(x) for x in signal_noise_ratios]
+    if compression_ratios and signal_noise_ratios:
+        raise ValueError(
+            "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+            "allowed when performing lossy compression"
+        )
+    if len(compression_ratios) > 10 or len(signal_noise_ratios) > 10:
+        raise ValueError("More than 10 compression layers is not supported")
+
+    # The destination for the encoded J2K codestream, needs to support BinaryIO
+    dst = BytesIO()
+    return_code = Encode(
+        <cnp.PyArrayObject *> arr,
+        <PyObject *> dst,
+        bits_stored,
+        photometric_interpretation,
+        use_mct,
+        <PyObject *> compression_ratios,
+        <PyObject *> signal_noise_ratios,
+        codec_format,
+    )
+    return return_code, dst.getvalue()
diff --git a/openjpeg/utils.py b/openjpeg/utils.py
index 01ee427..fb8c147 100644
--- a/openjpeg/utils.py
+++ b/openjpeg/utils.py
@@ -1,9 +1,10 @@
 from enum import IntEnum
 from io import BytesIO
-from math import ceil
+import logging
+from math import ceil, log
 import os
 from pathlib import Path
-from typing import BinaryIO, Tuple, Union, TYPE_CHECKING, Any, Dict, cast
+from typing import BinaryIO, Tuple, Union, TYPE_CHECKING, Any, Dict, cast, List
 import warnings
 
 import numpy as np
@@ -15,11 +16,23 @@
     from pydicom.dataset import Dataset
 
 
+LOGGER = logging.getLogger(__name__)
+
+
 class Version(IntEnum):
     v1 = 1
     v2 = 2
 
 
+class PhotometricInterpretation(IntEnum):
+    MONOCHROME1 = 2
+    MONOCHROME2 = 2
+    PALETTE_COLOR = 2
+    RGB = 1
+    YBR_FULL = 3
+    YBR_FULL_422 = 3
+
+
 MAGIC_NUMBERS = {
     # JPEG 2000 codestream, has no header, .j2k, .jpc, .j2c
     b"\xff\x4f\xff\x51": 0,
@@ -28,6 +41,61 @@ class Version(IntEnum):
     b"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a": 2,
     # JPT, .jpt - shrug
 }
+DECODING_ERRORS = {
+    1: "failed to create the input stream",
+    2: "failed to setup the decoder",
+    3: "failed to read the header",
+    4: "failed to set the component indices",
+    5: "failed to set the decoded area",
+    6: "failed to decode image",
+    7: "support for more than 16-bits per component is not implemented",
+    8: "failed to upscale subsampled components",
+}
+ENCODING_ERRORS = {
+    # Validation errors
+    1: (
+        "the input array has an unsupported number of samples per pixel, "
+        "must be 1, 3 or 4"
+    ),
+    2: (
+        "the input array has an invalid shape, must be (rows, columns) or "
+        "(rows, columns, planes)"
+    ),
+    3: ("the input array has an unsupported number of rows, must be " "in (1, 65535)"),
+    4: (
+        "the input array has an unsupported number of columns, must be " "in (1, 65535)"
+    ),
+    5: (
+        "the input array has an unsupported dtype, only bool, u1, u2, u4, i1, i2"
+        " and i4 are supported"
+    ),
+    6: "the input array must use little endian byte ordering",
+    7: "the input array must be C-style, contiguous and aligned",
+    8: (
+        "the image precision given by bits stored must be in (1, itemsize of "
+        "the input array's dtype)"
+    ),
+    9: (
+        "the value of the 'photometric_interpretation' parameter is not valid "
+        "for the number of samples per pixel in the input array"
+    ),
+    10: "the valid of the 'codec_format' paramter is invalid",
+    11: "more than 100 'compression_ratios' is not supported",
+    12: "invalid item in the 'compression_ratios' value",
+    13: "invalid compression ratio, must be in the range (1, 1000)",
+    14: "more than 100 'signal_noise_ratios' is not supported",
+    15: "invalid item in the 'signal_noise_ratios' value",
+    16: "invalid signal-to-noise ratio, must be in the range (0, 1000)",
+    # Encoding errors
+    20: "failed to assign the image component parameters",
+    21: "failed to create an empty image object",
+    22: "failed to set the encoding handler",
+    23: "failed to set up the encoder",
+    24: "failed to create the output stream",
+    25: "failure result from 'opj_start_compress()'",
+    26: "failure result from 'opj_encode()'",
+    27: "failure result from 'opj_endt_compress()'",
+}
 
 
 def _get_format(stream: BinaryIO) -> int:
@@ -135,9 +203,14 @@ def decode(
     if j2k_format not in [0, 1, 2]:
         raise ValueError(f"Unsupported 'j2k_format' value: {j2k_format}")
 
-    arr = cast(np.ndarray, _openjpeg.decode(buffer, j2k_format, as_array=True))
+    return_code, arr = _openjpeg.decode(buffer, j2k_format, as_array=True)
+    if return_code != 0:
+        raise RuntimeError(
+            f"Error decoding the J2K data: {DECODING_ERRORS.get(return_code, return_code)}"
+        )
+
     if not reshape:
-        return arr
+        return cast(np.ndarray, arr)
 
     meta = get_parameters(buffer, j2k_format)
     precision = cast(int, meta["precision"])
@@ -147,6 +220,7 @@ def decode(
     pixel_representation = cast(bool, meta["is_signed"])
     bpp = ceil(precision / 8)
 
+    bpp = 4 if bpp == 3 else bpp
     dtype = f"u{bpp}" if not pixel_representation else f"i{bpp}"
     arr = arr.view(dtype)
 
@@ -154,7 +228,7 @@ def decode(
     if pixels_per_sample > 1:
         shape.append(pixels_per_sample)
 
-    return arr.reshape(*shape)
+    return cast(np.ndarray, arr.reshape(*shape))
 
 
 def decode_pixel_data(
@@ -206,7 +280,11 @@ def decode_pixel_data(
                 "non-conformant to the DICOM Standard (Part 5, Annex A.4.4)"
             )
 
-        arr = _openjpeg.decode(buffer, j2k_format, as_array=True)
+        return_code, arr = _openjpeg.decode(buffer, j2k_format, as_array=True)
+        if return_code != 0:
+            raise RuntimeError(
+                f"Error decoding the J2K data: {DECODING_ERRORS.get(return_code, return_code)}"
+            )
 
         samples_per_pixel = kwargs.get("samples_per_pixel")
         bits_stored = kwargs.get("bits_stored")
@@ -253,7 +331,13 @@ def decode_pixel_data(
         return cast(np.ndarray, arr)
 
     # Version 2
-    return cast(bytearray, _openjpeg.decode(buffer, j2k_format, as_array=False))
+    return_code, buffer = _openjpeg.decode(buffer, j2k_format, as_array=False)
+    if return_code != 0:
+        raise RuntimeError(
+            f"Error decoding the J2K data: {DECODING_ERRORS.get(return_code, return_code)}"
+        )
+
+    return cast(bytearray, buffer)
 
 
 def get_parameters(
@@ -319,3 +403,178 @@ def get_parameters(
         Dict[str, Union[str, int, bool]],
         _openjpeg.get_parameters(buffer, j2k_format),
     )
+
+
+def _get_bits_stored(arr: np.ndarray) -> int:
+    """Return a 'bits_stored' appropriate for `arr`."""
+    if arr.dtype.kind == "b":
+        return 1
+
+    maximum = arr.max()
+    if arr.dtype.kind == "u":
+        if maximum == 0:
+            return 1
+
+        return int(log(maximum, 2) + 1)
+
+    minimum = arr.min()
+    for bit_depth in range(1, arr.dtype.itemsize * 8):
+        if maximum <= 2 ** (bit_depth - 1) - 1 and minimum >= -(2 ** (bit_depth - 1)):
+            return bit_depth
+
+    return cast(int, arr.dtype.itemsize * 8)
+
+
+def encode(
+    arr: np.ndarray,
+    bits_stored: Union[int, None] = None,
+    photometric_interpretation: int = 0,
+    use_mct: bool = True,
+    compression_ratios: Union[List[float], None] = None,
+    signal_noise_ratios: Union[List[float], None] = None,
+    codec_format: int = 0,
+    **kwargs: Any,
+) -> bytes:
+    """Return the JPEG 2000 compressed `arr`.
+
+    Encoding of the input array will use lossless compression by default, to
+    use lossy compression either `compression_ratios` or `signal_noise_ratios`
+    must be supplied.
+
+    Parameters
+    ----------
+    arr : numpy.ndarray
+        The array containing the image data to be encoded. 1-bit data should be
+        unpacked (if packed) and stored as a bool or u1 dtype.
+    bits_stored : int, optional
+        The bit-depth of the image, defaults to the minimum bit-depth required
+        to fully cover the range of pixel data in `arr`.
+    photometric_interpretation : int, optional
+        The colour space of the unencoded image data that will be set in the
+        JPEG 2000 metadata. If you are encoded RGB or YCbCr data then it's
+        strongly recommended that you select the correct colour space so
+        that anyone decoding your image data will know which colour space
+        transforms to apply. One of:
+
+        * ``0``: Unspecified
+        * ``1``: sRGB
+        * ``2``: Greyscale
+        * ``3``: sYCC (YCbCr)
+        * ``4``: eYCC
+        * ``5``: CMYK
+    use_mct : bool, optional
+        Apply multi-component transformation (MCT) prior to encoding the image
+        data. Defaults to ``True`` when the `photometric_interpretation` is
+        ``1`` as it is intended for use with RGB data and should result in
+        smaller file sizes. For all other values of `photometric_interpretation`
+        the value of `use_mct` will be ignored and MCT not applied.
+
+        If MCT is applied then the corresponding value of (0028,0004)
+        *Photometric Interpretation* is:
+
+        * ``"YBR_RCT"`` for lossless encoding
+        * ``"YBR_ICT"`` for lossy encoding
+
+        If MCT is not applied then *Photometric Intrepretation* should be the
+        value corresponding to the unencoded dataset.
+    compression_ratios : list[float], optional
+        Required if using lossy encoding, this is the compression ratio to use
+        for each layer. Should be in decreasing order (such as ``[80, 30, 10]``)
+        and the final value may be ``1`` to indicate lossless encoding should
+        be used for that layer. Cannot be used with `signal_noise_ratios`.
+    signal_noise_ratios : list[float], optional
+        Required if using lossy encoding, this is the desired peak
+        signal-to-noise ratio to use for each layer. Should be in increasing
+        order (such as ``[30, 40, 50]``), except for the last layer which may
+        be ``0`` to indicate lossless encoding should be used for that layer.
+        Cannot be used with `compression_ratios`.
+    codec_format : int, optional
+        The codec to used when encoding:
+
+        * ``0``: JPEG 2000 codestream only (default) (J2K/J2C format)
+        * ``2``: A boxed JPEG 2000 codestream (JP2 format)
+
+    Returns
+    -------
+    bytes
+        The encoded JPEG 2000 image data.
+    """
+    if compression_ratios is None:
+        compression_ratios = []
+
+    if signal_noise_ratios is None:
+        signal_noise_ratios = []
+
+    if arr.dtype.kind not in ("b", "i", "u"):
+        raise ValueError(
+            f"The input array has an unsupported dtype '{arr.dtype}', only "
+            "bool, u1, u2, u4, i1, i2 and i4 are supported"
+        )
+
+    if bits_stored is None:
+        bits_stored = _get_bits_stored(arr)
+
+    # The destination for the encoded data, must support BinaryIO
+    return_code, buffer = _openjpeg.encode(
+        arr,
+        bits_stored,
+        photometric_interpretation,
+        use_mct,
+        compression_ratios,
+        signal_noise_ratios,
+        codec_format,
+    )
+
+    if return_code != 0:
+        raise RuntimeError(
+            f"Error encoding the data: {ENCODING_ERRORS.get(return_code, return_code)}"
+        )
+
+    return cast(bytes, buffer)
+
+
+def encode_pixel_data(arr: np.ndarray, **kwargs: Any) -> bytes:
+    """Return the JPEG 2000 compressed `arr`.
+
+    .. versionadded:: 2.1
+
+    Parameters
+    ----------
+    src : numpy.ndarray
+        An array containing a single frame of image data to be encoded.
+    **kwargs
+        The following keyword arguments are optional:
+
+        * ``'photometric_interpretation'``: int - the colour space of the
+          unencoded image in `arr`, one of the following:
+
+          * ``0``: Unspecified
+          * ``1``: sRGB
+          * ``2``: Greyscale
+          * ``3``: sYCC (YCbCr)
+          * ``4``: eYCC
+          * ``5``: CMYK
+
+          It is strongly recommended you supply an appropriate
+          `photometric_interpretation`.
+        * ``'bits_stored'``: int - the bit-depth of the pixels in the image,
+          if not supplied then the smallest bit-depth that covers the full range
+          of pixel data in `arr` will be used.
+        * ``'lossless'``: bool: ``True`` to use lossless encoding (default),
+          ``False`` for lossy encoding. If using lossy encoding then
+          `compression_ratios` is required.
+        * ```use_mct': bool: ``True`` to use MCT with RGB images (default),
+          ``False`` otherwise.
+        * ``'codec_format'``: int - 0 for a JPEG2000 codestream (default),
+          1 for a JP2 codestream
+        * ''`compression_ratios'``: list[float] - required if `lossless` is
+          ``False``. Should be a list of the desired compression ratio per layer
+          in decreasing values. The final layer may be 1 for lossless
+          compression of that layer. Minimum value is 1 (for lossless).
+
+    Returns
+    -------
+    bytes
+        The JPEG 2000 encoded image data.
+    """
+    return encode(arr, **kwargs)
diff --git a/pyproject.toml b/pyproject.toml
index 0d4e107..9700362 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,7 +48,7 @@ packages = [
     {include = "openjpeg" },
 ]
 readme = "README.md"
-version = "2.1.0.dev0"
+version = "2.1.0"
 
 [tool.poetry.dependencies]
 python = "^3.8"
@@ -64,6 +64,9 @@ openjpeg = "openjpeg:decode"
 "1.2.840.10008.1.2.4.202" = "openjpeg:decode_pixel_data"
 "1.2.840.10008.1.2.4.203" = "openjpeg:decode_pixel_data"
 
+[tool.poetry.plugins."pylibjpeg.pixel_data_encoders"]
+"1.2.840.10008.1.2.4.90" = "openjpeg:encode_pixel_data"
+"1.2.840.10008.1.2.4.91" = "openjpeg:encode_pixel_data"
 
 [tool.coverage.run]
 omit = [


Test patch:
diff --git a/openjpeg/tests/test_decode.py b/openjpeg/tests/test_decode.py
index 4d09c19..29e5ede 100644
--- a/openjpeg/tests/test_decode.py
+++ b/openjpeg/tests/test_decode.py
@@ -96,6 +96,9 @@ def test_bad_decode():
     with pytest.raises(RuntimeError, match=msg):
         decode(frame)
 
+    with pytest.raises(RuntimeError, match=msg):
+        decode_pixel_data(frame, version=2)
+
 
 class TestDecode:
     """General tests for decode."""
@@ -338,6 +341,26 @@ def test_decode_pixel_data(self):
                 71,
             ]
 
+    def test_decode_pixel_data_raises(self):
+        """Test decode_pixel_data"""
+        d = DIR_15444 / "2KLS"
+        with (d / "693.j2k").open("rb") as f:
+            buffer = decode_pixel_data(f.read(), version=2)
+            assert isinstance(buffer, bytearray)
+            arr = np.frombuffer(buffer, dtype="i2").reshape((512, 512))
+            assert arr[270, 55:65].tolist() == [
+                340,
+                815,
+                1229,
+                1358,
+                1351,
+                1302,
+                1069,
+                618,
+                215,
+                71,
+            ]
+
 
 @pytest.mark.skipif(not HAS_PYDICOM, reason="No pydicom")
 class TestDecodeDCM:
diff --git a/openjpeg/tests/test_encode.py b/openjpeg/tests/test_encode.py
new file mode 100644
index 0000000..ee7c2af
--- /dev/null
+++ b/openjpeg/tests/test_encode.py
@@ -0,0 +1,809 @@
+import math
+from struct import unpack
+
+import numpy as np
+import pytest
+
+from openjpeg.data import JPEG_DIRECTORY
+from openjpeg.utils import (
+    encode,
+    encode_pixel_data,
+    decode,
+    PhotometricInterpretation as PI,
+    _get_bits_stored,
+)
+
+
+DIR_15444 = JPEG_DIRECTORY / "15444"
+
+
+def parse_j2k(buffer):
+    # SOC -> SIZ -> COD -> (COC) -> QCD -> (QCC) -> (RGN)
+    # soc = buffer[:2]  # SOC box, 0xff 0x4f
+
+    # Should be at the start of the SIZ marker
+    # siz = buffer[2:4]  # 0xff 0x51
+    # l_siz = buffer[4:6]  # length of SIZ box
+    # r_siz = buffer[6:8]
+    # x_siz = buffer[8:12]
+    # y_siz = buffer[12:16]
+    # xo_siz = buffer[16:20]
+    # yo_siz = buffer[20:24]
+    # xt_siz = buffer[24:28]
+    # yt_siz = buffer[28:32]
+    # xto_siz = buffer[32:36]
+    # yto_siz = buffer[36:40]
+    c_siz = buffer[40:42]
+    nr_components = unpack(">H", c_siz)[0]
+
+    o = 42
+    for component in range(nr_components):
+        ssiz = buffer[o]
+        # xrsiz = buffer[o + 1]
+        # yrsiz = buffer[o + 2]
+        o += 3
+
+    # Should be at the start of the COD marker
+    # cod = buffer[o : o + 2]
+    # l_cod = buffer[o + 2 : o + 4]
+    # s_cod = buffer[o + 4 : o + 5]
+    sg_cod = buffer[o + 5 : o + 9]
+
+    # progression_order = sg_cod[0]
+    nr_layers = sg_cod[1:3]
+    mct = sg_cod[3]  # 0 for none, 1 for applied
+
+    param = {}
+    if ssiz & 0x80:
+        param["precision"] = (ssiz & 0x7F) + 1
+        param["is_signed"] = True
+    else:
+        param["precision"] = ssiz + 1
+        param["is_signed"] = False
+
+    param["components"] = nr_components
+    param["mct"] = bool(mct)
+    param["layers"] = unpack(">H", nr_layers)[0]
+
+    return param
+
+
+class TestEncode:
+    """Tests for encode()"""
+
+    def test_invalid_shape_raises(self):
+        """Test invalid array shapes raise exceptions."""
+        msg = (
+            "Error encoding the data: the input array has an invalid shape, "
+            r"must be \(rows, columns\) or \(rows, columns, planes\)"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1,), dtype="u1"))
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2, 3, 4), dtype="u1"))
+
+    def test_invalid_samples_per_pixel_raises(self):
+        """Test invalid samples per pixel raise exceptions."""
+        msg = (
+            "Error encoding the data: the input array has an unsupported number "
+            "of samples per pixel, must be 1, 3 or 4"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2, 2), dtype="u1"))
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2, 5), dtype="u1"))
+
+    def test_invalid_dtype_raises(self):
+        """Test invalid array dtype raise exceptions."""
+        msg = "input array has an unsupported dtype"
+        for dtype in ("u8", "i8", "f", "d", "c", "U", "m", "M"):
+            with pytest.raises((ValueError, RuntimeError), match=msg):
+                encode(np.ones((1, 2), dtype=dtype))
+
+    def test_invalid_contiguity_raises(self):
+        """Test invalid array contiguity raise exceptions."""
+        msg = (
+            "Error encoding the data: the input array must be C-style, "
+            "contiguous and aligned"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((3, 3), dtype="u1").T)
+
+    def test_invalid_dimensions_raises(self):
+        """Test invalid array dimensions raise exceptions."""
+        msg = (
+            "Error encoding the data: the input array has an unsupported number "
+            r"of rows, must be in \(1, 65535\)"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((65536, 1), dtype="u1"))
+
+        msg = (
+            "Error encoding the data: the input array has an unsupported number "
+            r"of columns, must be in \(1, 65535\)"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 65536), dtype="u1"))
+
+    def test_invalid_bits_stored_raises(self):
+        """Test invalid bits_stored"""
+        msg = (
+            "Invalid value for the 'bits_stored' parameter, the value must "
+            r"be in the range \(1, 8\)"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), bits_stored=0)
+
+        with pytest.raises(ValueError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), bits_stored=9)
+
+        msg = (
+            "A 'bits_stored' value of 15 is incompatible with the range of "
+            r"pixel data in the input array: \(-16385, 2\)"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([-(2**14) - 1, 2], dtype="i2"), bits_stored=15)
+
+        msg = (
+            "A 'bits_stored' value of 15 is incompatible with the range of "
+            r"pixel data in the input array: \(-16384, 16384\)"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([-(2**14), 2**14], dtype="i2"), bits_stored=15)
+
+        msg = (
+            "A 'bits_stored' value of 4 is incompatible with the range of "
+            r"pixel data in the input array: \(0, 16\)"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([0, 2**4], dtype="u2"), bits_stored=4)
+
+    def test_invalid_pixel_value_raises(self):
+        """Test invalid pixel values raise exceptions."""
+        msg = (
+            "The input array contains values outside the range of the maximum "
+            "supported bit-depth of 24"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([2**24, 2], dtype="u4"))
+
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([2**23, 2], dtype="i4"))
+
+        with pytest.raises(ValueError, match=msg):
+            encode(np.asarray([-(2**23) - 1, 2], dtype="i4"))
+
+    def test_compression_snr_raises(self):
+        """Test using compression_ratios and signal_noise_ratios raises."""
+        msg = (
+            "Only one of 'compression_ratios' or 'signal_noise_ratios' is "
+            "allowed when performing lossy compression"
+        )
+        with pytest.raises(ValueError, match=msg):
+            encode(
+                np.asarray([0, 2], dtype="u2"),
+                compression_ratios=[2, 1],
+                signal_noise_ratios=[1, 2],
+            )
+
+    def test_invalid_photometric_raises(self):
+        """Test invalid photometric_interpretation raises."""
+        msg = (
+            "Error encoding the data: the value of the 'photometric_interpretation' "
+            "parameter is not valid for the number of samples per pixel in the "
+            "input array"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), photometric_interpretation=PI.RGB)
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(
+                np.ones((1, 2, 3), dtype="u1"),
+                photometric_interpretation=PI.MONOCHROME2,
+            )
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(
+                np.ones((1, 2, 4), dtype="u1"),
+                photometric_interpretation=PI.MONOCHROME2,
+            )
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2, 4), dtype="u1"), photometric_interpretation=PI.RGB)
+
+    def test_invalid_codec_format_raises(self):
+        """Test an invalid 'codec_format' raises and exception."""
+        msg = "The value of the 'codec_format' parameter is invalid, must be 0 or 2"
+        with pytest.raises(ValueError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), codec_format=1)
+
+    def test_invalid_compression_ratios_raises(self):
+        """Test an invalid 'compression_ratios' raises exceptions."""
+        msg = "More than 10 compression layers is not supported"
+        with pytest.raises(ValueError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), compression_ratios=[1] * 11)
+
+        msg = (
+            "Error encoding the data: invalid compression ratio, must be in the "
+            r"range \(1, 1000\)"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), compression_ratios=[0])
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), compression_ratios=[1001])
+
+    def test_invalid_signal_noise_ratios_raises(self):
+        """Test an invalid 'signal_noise_ratios' raises exceptions."""
+        msg = "More than 10 compression layers is not supported"
+        with pytest.raises(ValueError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), signal_noise_ratios=[1] * 11)
+
+        msg = (
+            "Error encoding the data: invalid signal-to-noise ratio, must be "
+            r"in the range \(0, 1000\)"
+        )
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), signal_noise_ratios=[-1])
+
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"), signal_noise_ratios=[1001])
+
+    def test_encoding_failures_raise(self):
+        """Miscellaneous test to check that failures are handled properly."""
+        # Not exhaustive! Missing coverage for
+        #   assigning image components
+        #   creating the empty image
+        #   setting the encoder
+        #   creating the output stream
+        #   opj_encode()
+        #   opj_end_compress()
+
+        # Input too small
+        msg = r"Error encoding the data: failure result from 'opj_start_compress\(\)'"
+        with pytest.raises(RuntimeError, match=msg):
+            encode(np.ones((1, 2), dtype="u1"))
+
+    def test_mct(self):
+        """Test that MCT is applied as required."""
+        # Should only be applied with RGB
+        arr = np.random.randint(0, 2**8 - 1, size=(100, 100, 3), dtype="u1")
+        buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=True)
+        param = parse_j2k(buffer)
+        assert param["mct"] is True
+
+        buffer = encode(
+            arr,
+            photometric_interpretation=PI.RGB,
+            use_mct=True,
+            compression_ratios=[2.5, 3, 5],
+        )
+        param = parse_j2k(buffer)
+        assert param["mct"] is True
+
+        buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+        buffer = encode(
+            arr,
+            photometric_interpretation=PI.RGB,
+            use_mct=False,
+            compression_ratios=[2.5, 3, 5],
+        )
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+        for pi in (0, 3, 4):
+            buffer = encode(arr, photometric_interpretation=pi, use_mct=True)
+            param = parse_j2k(buffer)
+            assert param["mct"] is False
+
+            buffer = encode(
+                arr,
+                photometric_interpretation=pi,
+                use_mct=True,
+                compression_ratios=[2.5, 3, 5],
+            )
+            param = parse_j2k(buffer)
+            assert param["mct"] is False
+
+        arr = np.random.randint(0, 2**8 - 1, size=(100, 100), dtype="u1")
+        buffer = encode(arr, photometric_interpretation=PI.MONOCHROME1, use_mct=True)
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+        buffer = encode(
+            arr,
+            photometric_interpretation=PI.MONOCHROME1,
+            use_mct=True,
+            compression_ratios=[2.5, 3, 5],
+        )
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+        arr = np.random.randint(0, 2**8 - 1, size=(100, 100, 4), dtype="u1")
+        buffer = encode(arr, photometric_interpretation=5, use_mct=True)
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+        buffer = encode(
+            arr,
+            photometric_interpretation=5,
+            use_mct=True,
+            compression_ratios=[2.5, 3, 5],
+        )
+        param = parse_j2k(buffer)
+        assert param["mct"] is False
+
+    def test_lossless_bool(self):
+        """Test encoding bool data for bit-depth 1"""
+        rows = 123
+        cols = 234
+        planes = 3
+        arr = np.random.randint(0, high=1, size=(rows, cols), dtype="bool")
+        buffer = encode(arr, photometric_interpretation=PI.MONOCHROME2)
+        out = decode(buffer)
+        param = parse_j2k(buffer)
+        assert param["precision"] == 1
+        assert param["is_signed"] is False
+        assert param["layers"] == 1
+        assert param["components"] == 1
+
+        assert out.dtype.kind == "u"
+        assert np.array_equal(arr, out)
+
+        arr = np.random.randint(0, high=1, size=(rows, cols, planes), dtype="bool")
+        buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+        out = decode(buffer)
+        param = parse_j2k(buffer)
+        assert param["precision"] == 1
+        assert param["is_signed"] is False
+        assert param["layers"] == 1
+        assert param["components"] == 3
+
+        assert out.dtype.kind == "u"
+        assert np.array_equal(arr, out)
+
+    def test_lossless_unsigned(self):
+        """Test encoding unsigned data for bit-depth 1-16"""
+        rows = 123
+        cols = 234
+        for bit_depth in range(1, 17):
+            maximum = 2**bit_depth - 1
+            dtype = f"u{math.ceil(bit_depth / 8)}"
+            arr = np.random.randint(0, high=maximum, size=(rows, cols), dtype=dtype)
+            buffer = encode(arr, photometric_interpretation=PI.MONOCHROME2)
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 1
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "u"
+            assert np.array_equal(arr, out)
+
+            arr = np.random.randint(0, high=maximum, size=(rows, cols, 3), dtype=dtype)
+            buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 1
+            assert param["components"] == 3
+
+            assert out.dtype.kind == "u"
+            assert np.array_equal(arr, out)
+
+            arr = np.random.randint(0, high=maximum, size=(rows, cols, 4), dtype=dtype)
+            buffer = encode(arr, photometric_interpretation=5, use_mct=False)
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 1
+            assert param["components"] == 4
+
+            assert out.dtype.kind == "u"
+            assert np.array_equal(arr, out)
+
+    def test_lossless_unsigned_u4(self):
+        """Test encoding unsigned data for bit-depth 17-32"""
+        rows = 123
+        cols = 234
+        planes = 3
+        for bit_depth in range(17, 25):
+            maximum = 2**bit_depth - 1
+            arr = np.random.randint(0, high=maximum, size=(rows, cols), dtype="u4")
+            buffer = encode(arr, photometric_interpretation=PI.MONOCHROME2)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 1
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "u"
+            assert np.array_equal(arr, out)
+
+            arr = np.random.randint(
+                0, high=maximum, size=(rows, cols, planes), dtype="u4"
+            )
+            buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 1
+            assert param["components"] == 3
+
+            assert out.dtype.kind == "u"
+            assert np.array_equal(arr, out)
+
+    def test_lossless_signed(self):
+        """Test encoding signed data for bit-depth 1-16"""
+        rows = 123
+        cols = 543
+        for bit_depth in range(1, 17):
+            maximum = 2 ** (bit_depth - 1) - 1
+            minimum = -(2 ** (bit_depth - 1))
+            dtype = f"i{math.ceil(bit_depth / 8)}"
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols), dtype=dtype
+            )
+            buffer = encode(arr, photometric_interpretation=PI.MONOCHROME2)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 1
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "i"
+            assert np.array_equal(arr, out)
+
+            maximum = 2 ** (bit_depth - 1) - 1
+            minimum = -(2 ** (bit_depth - 1))
+            dtype = f"i{math.ceil(bit_depth / 8)}"
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols, 3), dtype=dtype
+            )
+            buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 1
+            assert param["components"] == 3
+
+            assert out.dtype.kind == "i"
+            assert np.array_equal(arr, out)
+
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols, 4), dtype=dtype
+            )
+            buffer = encode(arr, photometric_interpretation=5, use_mct=False)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 1
+            assert param["components"] == 4
+
+            assert out.dtype.kind == "i"
+            assert np.array_equal(arr, out)
+
+    def test_lossless_signed_u4(self):
+        """Test encoding signed data for bit-depth 17-32"""
+        rows = 123
+        cols = 234
+        planes = 3
+        for bit_depth in range(17, 25):
+            maximum = 2 ** (bit_depth - 1) - 1
+            minimum = -(2 ** (bit_depth - 1))
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols), dtype="i4"
+            )
+            buffer = encode(arr, photometric_interpretation=PI.MONOCHROME2)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 1
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "i"
+            assert np.array_equal(arr, out)
+
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols, planes), dtype="i4"
+            )
+            buffer = encode(arr, photometric_interpretation=PI.RGB, use_mct=False)
+            out = decode(buffer)
+
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 1
+            assert param["components"] == 3
+
+            assert out.dtype.kind == "i"
+            assert np.array_equal(arr, out)
+
+    def test_lossy_unsigned(self):
+        """Test lossy encoding with unsigned data"""
+        rows = 123
+        cols = 234
+        for bit_depth in range(1, 17):
+            maximum = 2**bit_depth - 1
+            dtype = f"u{math.ceil(bit_depth / 8)}"
+            arr = np.random.randint(0, high=maximum, size=(rows, cols), dtype=dtype)
+            buffer = encode(arr, compression_ratios=[4, 2, 1])
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 3
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "u"
+            assert np.allclose(arr, out, atol=5)
+
+            buffer = encode(arr, signal_noise_ratios=[50, 100, 200])
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is False
+            assert param["layers"] == 3
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "u"
+            assert np.allclose(arr, out, atol=5)
+
+    def test_lossy_signed(self):
+        """Test lossy encoding with unsigned data"""
+        rows = 123
+        cols = 234
+        for bit_depth in range(1, 17):
+            maximum = 2 ** (bit_depth - 1) - 1
+            minimum = -(2 ** (bit_depth - 1))
+            dtype = f"i{math.ceil(bit_depth / 8)}"
+            arr = np.random.randint(
+                low=minimum, high=maximum, size=(rows, cols), dtype=dtype
+            )
+            buffer = encode(arr, compression_ratios=[4, 2, 1])
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 3
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "i"
+            assert np.allclose(arr, out, atol=5)
+
+            buffer = encode(arr, signal_noise_ratios=[50, 100, 200])
+            out = decode(buffer)
+            param = parse_j2k(buffer)
+            assert param["precision"] == bit_depth
+            assert param["is_signed"] is True
+            assert param["layers"] == 3
+            assert param["components"] == 1
+
+            assert out.dtype.kind == "i"
+            assert np.allclose(arr, out, atol=5)
+
+    def test_roundtrip_u1_ybr(self):
+        """Test a round trip for u1 YBR."""
+        jpg = DIR_15444 / "2KLS" / "oj36.j2k"
+        with open(jpg, "rb") as f:
+            data = f.read()
+
+        arr = decode(data)
+        assert "uint8" == arr.dtype
+        assert (256, 256, 3) == arr.shape
+
+        # Test lossless
+        result = encode(arr, photometric_interpretation=PI.YBR_FULL)
+        out = decode(result)
+        assert np.array_equal(arr, out)
+
+        # Test lossy
+        result = encode(
+            arr,
+            photometric_interpretation=PI.YBR_FULL,
+            lossless=0,
+            compression_ratios=[6, 4, 2, 1],
+        )
+        out = decode(result)
+        assert np.allclose(out, arr, atol=2)
+
+        # Test lossy
+        result = encode(
+            arr,
+            photometric_interpretation=PI.YBR_FULL,
+            lossless=0,
+            compression_ratios=[80, 100, 150],
+        )
+        out = decode(result)
+        assert np.allclose(out, arr, atol=2)
+
+    def test_roundtrip_i2_mono(self):
+        """Test a round trip for i2 YBR."""
+
+        jpg = DIR_15444 / "2KLS" / "693.j2k"
+        with open(jpg, "rb") as f:
+            data = f.read()
+
+        arr = decode(data)
+        assert "i2" == arr.dtype
+        assert (512, 512) == arr.shape
+
+        # Test lossless
+        result = encode(
+            arr,
+            photometric_interpretation=PI.MONOCHROME2,
+        )
+        out = decode(result)
+        assert np.array_equal(arr, out)
+
+        # Test lossy w/ compression ratios
+        result = encode(
+            arr,
+            photometric_interpretation=PI.MONOCHROME2,
+            compression_ratios=[6, 4, 2, 1],
+        )
+        out = decode(result)
+        assert np.allclose(out, arr, atol=2)
+
+        # Test lossy w/ signal-to-noise ratios
+        result = encode(
+            arr,
+            photometric_interpretation=PI.MONOCHROME2,
+            signal_noise_ratios=[80, 100],
+        )
+        out = decode(result)
+        assert np.allclose(out, arr, atol=2)
+
+
+class TestEncodePixelData:
+    """Tests for encode_pixel_data()"""
+
+    def test_nominal(self):
+        """Test the function works OK"""
+        arr = np.random.randint(0, high=65535, size=(100, 100), dtype="u2")
+        buffer = encode_pixel_data(arr)
+        assert np.array_equal(arr, decode(buffer))
+
+
+class TestGetBitsStored:
+    """Tests for _get_bits_stored()"""
+
+    def check_signed(self, nr_bits, minimin, minimax, maximax, maximin):
+        arr = np.asarray([minimin, minimax], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+        arr = np.asarray([minimax - 1, minimax], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+        arr = np.asarray([minimin, minimin + 1], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+        arr = np.asarray([maximin, maximax], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+        arr = np.asarray([0, maximax], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+        arr = np.asarray([minimin, 0], dtype="i2")
+        assert _get_bits_stored(arr) == nr_bits
+
+    def test_bool(self):
+        """Test bool input."""
+        arr = np.asarray([0, 0], dtype="bool")
+        assert _get_bits_stored(arr) == 1
+
+        arr = np.asarray([1, 1], dtype="bool")
+        assert _get_bits_stored(arr) == 1
+
+    def test_unsigned(self):
+        """Test unsigned integer input."""
+        arr = np.asarray([0, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 1
+        arr = np.asarray([1, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 1
+
+        arr = np.asarray([2, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 2
+        arr = np.asarray([3, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 2
+
+        arr = np.asarray([4, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 3
+        arr = np.asarray([7, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 3
+
+        arr = np.asarray([8, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 4
+        arr = np.asarray([15, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 4
+
+        arr = np.asarray([16, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 5
+        arr = np.asarray([31, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 5
+
+        arr = np.asarray([32, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 6
+        arr = np.asarray([63, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 6
+
+        arr = np.asarray([64, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 7
+        arr = np.asarray([127, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 7
+
+        arr = np.asarray([128, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 8
+        arr = np.asarray([255, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 8
+
+        arr = np.asarray([256, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 9
+        arr = np.asarray([511, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 9
+
+        arr = np.asarray([512, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 10
+        arr = np.asarray([1023, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 10
+
+        arr = np.asarray([1024, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 11
+        arr = np.asarray([2047, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 11
+
+        arr = np.asarray([2048, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 12
+        arr = np.asarray([4095, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 12
+
+        arr = np.asarray([4096, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 13
+        arr = np.asarray([8191, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 13
+
+        arr = np.asarray([8192, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 14
+        arr = np.asarray([16383, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 14
+
+        arr = np.asarray([16384, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 15
+        arr = np.asarray([32767, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 15
+
+        arr = np.asarray([32768, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 16
+        arr = np.asarray([65535, 0], dtype="u2")
+        assert _get_bits_stored(arr) == 16
+
+    def test_signed(self):
+        """Test signed integer input."""
+        arr = np.asarray([0, 0], dtype="i2")
+        assert _get_bits_stored(arr) == 1
+        arr = np.asarray([-1, 0], dtype="i2")
+        assert _get_bits_stored(arr) == 1
+
+        minimin, minimax = -2, 1
+        maximax, maximin = 1, -2
+        self.check_signed(2, minimin, minimax, maximax, maximin)
+
+        for ii in range(3, 17):
+            minimin, minimax = maximin - 1, maximax + 1
+            maximax, maximin = 2 ** (ii - 1) - 1, -(2 ** (ii - 1))
+            self.check_signed(ii, minimin, minimax, maximax, maximin)
diff --git a/openjpeg/tests/test_misc.py b/openjpeg/tests/test_misc.py
new file mode 100644
index 0000000..438084e
--- /dev/null
+++ b/openjpeg/tests/test_misc.py
@@ -0,0 +1,24 @@
+"""Tests for standalone decoding."""
+
+import logging
+
+from openjpeg import debug_logger
+
+
+def test_debug_logger():
+    """Test __init__.debug_logger()."""
+    logger = logging.getLogger("openjpeg")
+    assert len(logger.handlers) == 1
+    assert isinstance(logger.handlers[0], logging.NullHandler)
+
+    debug_logger()
+
+    assert len(logger.handlers) == 1
+    assert isinstance(logger.handlers[0], logging.StreamHandler)
+
+    debug_logger()
+
+    assert len(logger.handlers) == 1
+    assert isinstance(logger.handlers[0], logging.StreamHandler)
+
+    logger.handlers = []