Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-imagecodecs for 
openSUSE:Factory checked in at 2023-09-14 16:26:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-imagecodecs (Old)
 and      /work/SRC/openSUSE:Factory/.python-imagecodecs.new.1766 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-imagecodecs"

Thu Sep 14 16:26:05 2023 rev:12 rq:1111059 version:2023.3.16

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-imagecodecs/python-imagecodecs.changes    
2023-09-13 20:47:55.353261085 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-imagecodecs.new.1766/python-imagecodecs.changes
  2023-09-14 16:29:02.493818131 +0200
@@ -1,0 +2,10 @@
+Wed Sep 13 11:57:38 UTC 2023 - Markéta Machová <mmach...@suse.com>
+
+- Add patches for the compatibility with libavif 1.0.0:
+  * libavif.patch
+  * quantize.patch
+  * avif.patch
+  * tests.patch
+  * integrate.patch
+
+-------------------------------------------------------------------

New:
----
  avif.patch
  integrate.patch
  libavif.patch
  quantize.patch
  tests.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-imagecodecs.spec ++++++
--- /var/tmp/diff_new_pack.xO1S9z/_old  2023-09-14 16:29:04.101875576 +0200
+++ /var/tmp/diff_new_pack.xO1S9z/_new  2023-09-14 16:29:04.105875719 +0200
@@ -36,6 +36,16 @@
 Patch0:         always-cythonize.patch
 # PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/14bb6012a8c9f48df264ea996f3376e57166201a
 Update imagecodecs/_heif.pyx
 Patch1:         cython3.patch
+# PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/d04112759c48772c4d46a2dfa4f4c6a76e23c9a9
 Update imagecodecs/libavif.pxd
+Patch2:         libavif.patch
+# PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/93d1f751436e357d73eb6fdebc2af833059d9ea9
 Add imagecodecs/_quantize.pyx
+Patch3:         quantize.patch
+# PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/2f548c9a4df443948f2dfcde30a7211ce8b3adc2
 Update imagecodecs/_avif.pyx
+Patch4:         avif.patch
+# PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/0030b7b74fc17ceb356d1f67633ba1734108dac9
 Update tests/test_imagecodecs.py
+Patch5:         tests.patch
+# PATCH-FIX-UPSTREAM 
https://github.com/cgohlke/imagecodecs/commit/e9b5a984b72c9d4e14f9d37ec99389d25645c7fb
 Update imagecodecs/imagecodecs.py
+Patch6:         integrate.patch
 BuildRequires:  %{python_module Cython >= 0.29.19}
 BuildRequires:  %{python_module base >= 3.8}
 BuildRequires:  %{python_module numpy-devel}
@@ -60,9 +70,9 @@
 BuildRequires:  %{python_module Pillow}
 BuildRequires:  %{python_module blosc}
 BuildRequires:  %{python_module czifile}
-BuildRequires:  %{python_module dask if %python-base < 3.11}
-BuildRequires:  %{python_module dask-array if %python-base < 3.11}
-BuildRequires:  %{python_module dask-delayed if %python-base < 3.11}
+BuildRequires:  %{python_module dask-array}
+BuildRequires:  %{python_module dask-delayed}
+BuildRequires:  %{python_module dask}
 BuildRequires:  %{python_module imagecodecs >= %{version}}
 BuildRequires:  %{python_module lz4}
 BuildRequires:  %{python_module matplotlib >= 3.3}
@@ -102,6 +112,7 @@
 BuildRequires:  pkgconfig(bzip2)
 BuildRequires:  pkgconfig(cfitsio)
 BuildRequires:  pkgconfig(lcms2)
+BuildRequires:  pkgconfig(libavif) >= 1.0.0
 BuildRequires:  pkgconfig(libbrotlicommon)
 BuildRequires:  pkgconfig(libheif)
 # Beta, not available in minimum version
@@ -122,8 +133,6 @@
 BuildRequires:  zfp-devel
 BuildRequires:  pkgconfig(SvtAv1Dec)
 BuildRequires:  pkgconfig(SvtAv1Enc)
-# 32-bit tests fail
-BuildRequires:  pkgconfig(libavif)
 %endif
 %endif
 %python_subpackages
@@ -144,6 +153,9 @@
 %setup -q -n imagecodecs-%{version}
 # the patch from github requires unix line endings to apply
 dos2unix tests/test_imagecodecs.py
+dos2unix imagecodecs/libavif.pxd
+dos2unix imagecodecs/_avif.pyx
+dos2unix imagecodecs/imagecodecs.py
 %autopatch -p1
 
 cp %SOURCE1 ./
@@ -179,8 +191,6 @@
 %ifarch %ix86 %arm32
 donttest="$donttest or spng"
 %endif
-# no dask because of numba for python 3.11
-python311_donttest="or imagecodecs.imagecodecs"
 %pytest_arch -n auto tests -rsXfE --doctest-modules 
%{$python_sitearch}/imagecodecs/imagecodecs.py -k "not ($donttest 
${$python_donttest})"
 %endif
 

++++++ avif.patch ++++++
>From 2f548c9a4df443948f2dfcde30a7211ce8b3adc2 Mon Sep 17 00:00:00 2001
From: Christoph Gohlke <cgoh...@cgohlke.com>
Date: Sat, 2 Sep 2023 22:41:40 -0700
Subject: [PATCH] Update imagecodecs/_avif.pyx

---
 imagecodecs/_avif.pyx | 60 +++++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 28 deletions(-)

Index: imagecodecs-2023.3.16/imagecodecs/_avif.pyx
===================================================================
--- imagecodecs-2023.3.16.orig/imagecodecs/_avif.pyx
+++ imagecodecs-2023.3.16/imagecodecs/_avif.pyx
@@ -56,12 +56,14 @@ class AVIF:
         YUV422 = AVIF_PIXEL_FORMAT_YUV422
         YUV420 = AVIF_PIXEL_FORMAT_YUV420
         YUV400 = AVIF_PIXEL_FORMAT_YUV400
+        COUNT = AVIF_PIXEL_FORMAT_COUNT
 
-    class QUANTIZER(enum.IntEnum):
-        """AVIF codec quantizers."""
-        LOSSLESS = AVIF_QUANTIZER_LOSSLESS
-        BEST_QUALITY = AVIF_QUANTIZER_BEST_QUALITY
-        WORST_QUALITY = AVIF_QUANTIZER_WORST_QUALITY
+    class QUALITY(enum.IntEnum):
+        """AVIF codec quality."""
+        DEFAULT = AVIF_QUALITY_DEFAULT  # -1
+        LOSSLESS = AVIF_QUALITY_LOSSLESS  # 100
+        WORST = AVIF_QUALITY_WORST  # 0
+        BEST = AVIF_QUALITY_BEST  # 100
 
     class SPEED(enum.IntEnum):
         """AVIF codec speeds."""
@@ -85,6 +87,7 @@ class AVIF:
         LIBGAV1 = AVIF_CODEC_CHOICE_LIBGAV1
         RAV1E = AVIF_CODEC_CHOICE_RAV1E
         SVT = AVIF_CODEC_CHOICE_SVT
+        AVM = AVIF_CODEC_CHOICE_AVM
 
 
 class AvifError(RuntimeError):
@@ -143,8 +146,8 @@ def avif_encode(
         const uint8_t[::1] dst  # must be const to write to bytes
         ssize_t dstsize, size
         ssize_t itemsize = data.dtype.itemsize
+        int quality = AVIF_QUALITY_LOSSLESS
         int speed_ = AVIF_SPEED_DEFAULT
-        int quantizer = AVIF_QUANTIZER_LOSSLESS
         int tilerowslog2 = 0
         int tilecolslog2 = 0
         int duration = 1
@@ -172,10 +175,10 @@ def avif_encode(
         src.dtype in (numpy.uint8, numpy.uint16)
         # and numpy.PyArray_ISCONTIGUOUS(src)
         and src.ndim in (2, 3, 4)
-        and src.shape[0] < 2 ** 31
-        and src.shape[1] < 2 ** 31
-        and src.shape[src.ndim - 1] < 2 ** 31
-        and src.shape[src.ndim - 2] < 2 ** 31
+        and src.shape[0] <= 2147483647
+        and src.shape[1] <= 2147483647
+        and src.shape[src.ndim - 1] <= 2147483647
+        and src.shape[src.ndim - 2] <= 2147483647
     ):
         raise ValueError('invalid data shape, strides, or dtype')
 
@@ -205,10 +208,6 @@ def avif_encode(
     monochrome = samples < 3
     hasalpha = samples in (2, 4)
 
-    if monochrome:
-        raise NotImplementedError('cannot encode monochome images')
-        # TODO: check status of libavif/aom monochome support
-
     if bitspersample is None:
         depth = <uint32_t> itemsize * 8
     else:
@@ -223,11 +222,11 @@ def avif_encode(
         if 0 <= tilecolslog2 <= 6:
             raise ValueError('invalid tileColsLog2')
 
-    quantizer = _default_value(
+    quality = _default_value(
         level,
-        AVIF_QUANTIZER_LOSSLESS,
-        AVIF_QUANTIZER_BEST_QUALITY,
-        AVIF_QUANTIZER_WORST_QUALITY
+        AVIF_QUALITY_LOSSLESS,  # 100
+        AVIF_QUALITY_DEFAULT,  # -1
+        AVIF_QUALITY_BEST  # 100
     )
 
     speed_ = _default_value(
@@ -239,8 +238,8 @@ def avif_encode(
 
     if monochrome:
         yuvformat = AVIF_PIXEL_FORMAT_YUV400
-        quantizer = AVIF_QUANTIZER_LOSSLESS
-    elif quantizer == AVIF_QUANTIZER_LOSSLESS:
+        quality = AVIF_QUALITY_LOSSLESS
+    elif quality == AVIF_QUALITY_LOSSLESS:
         yuvformat = AVIF_PIXEL_FORMAT_YUV444
     elif pixelformat is not None:
         yuvformat = _avif_pixelformat(pixelformat)
@@ -254,11 +253,9 @@ def avif_encode(
             if encoder == NULL:
                 raise AvifError('avifEncoderCreate', 'NULL')
 
+            encoder.quality = quality
+            encoder.qualityAlpha = AVIF_QUALITY_LOSSLESS
             encoder.maxThreads = maxthreads
-            encoder.minQuantizer = quantizer
-            encoder.maxQuantizer = quantizer
-            encoder.minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS
-            encoder.maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS
             encoder.tileRowsLog2 = tilerowslog2
             encoder.tileColsLog2 = tilecolslog2
             encoder.speed = speed_
@@ -269,7 +266,7 @@ def avif_encode(
             if image == NULL:
                 raise AvifError('avifImageCreate', 'NULL')
 
-            if monochrome or quantizer == AVIF_QUANTIZER_LOSSLESS:
+            if monochrome or quality == AVIF_QUALITY_LOSSLESS:
                 if codecchoice == AVIF_CODEC_CHOICE_AUTO:
                     encoder.codecChoice = AVIF_CODEC_CHOICE_AOM
                 else:
@@ -287,7 +284,9 @@ def avif_encode(
 
             avifRGBImageSetDefaults(&rgb, image)
             if monochrome:
-                avifRGBImageAllocatePixels(&rgb)
+                res = avifRGBImageAllocatePixels(&rgb)
+                if res != AVIF_RESULT_OK:
+                    raise AvifError('avifRGBImageAllocatePixels', res)
                 if rgb.format != AVIF_RGB_FORMAT_RGBA:
                     raise RuntimeError('rgb.format != AVIF_RGB_FORMAT_RGBA')
                 srcptr = <uint8_t *> src.data
@@ -419,7 +418,7 @@ def avif_encode(
     return _return_output(out, dstsize, rawsize, outgiven)
 
 
-def avif_decode(data, index=None, out=None):
+def avif_decode(data, index=None, numthreads=None, out=None):
     """Return decoded AVIF image."""
     cdef:
         numpy.ndarray dst
@@ -429,6 +428,7 @@ def avif_decode(data, index=None, out=No
         ssize_t samples, size, itemsize, i, j, k, dstindex, imagecount
         bint monochrome = 0  # must be initialized
         bint hasalpha = 0
+        int maxthreads = <int> _default_threads(numthreads)
         uint8_t* dstptr = NULL
         uint8_t* srcptr = NULL
         avifDecoder* decoder = NULL
@@ -448,6 +448,8 @@ def avif_decode(data, index=None, out=No
             # required to read AVIF files created by ImageMagick
             decoder.strictFlags = AVIF_STRICT_DISABLED
 
+            decoder.maxThreads = maxthreads
+
             res = avifDecoderSetSource(decoder, AVIF_DECODER_SOURCE_AUTO)
             if res != AVIF_RESULT_OK:
                 raise AvifError('avifDecoderSetSource', res)
@@ -632,10 +634,12 @@ cdef _avif_codecchoice(codec):
         AVIF_CODEC_CHOICE_LIBGAV1: AVIF_CODEC_CHOICE_LIBGAV1,
         AVIF_CODEC_CHOICE_RAV1E: AVIF_CODEC_CHOICE_RAV1E,
         AVIF_CODEC_CHOICE_SVT: AVIF_CODEC_CHOICE_SVT,
+        AVIF_CODEC_CHOICE_AVM: AVIF_CODEC_CHOICE_AVM,
         'auto': AVIF_CODEC_CHOICE_AUTO,
         'aom': AVIF_CODEC_CHOICE_AOM,
         'dav1d': AVIF_CODEC_CHOICE_DAV1D,
         'libgav1': AVIF_CODEC_CHOICE_LIBGAV1,
         'rav1e': AVIF_CODEC_CHOICE_RAV1E,
         'svt': AVIF_CODEC_CHOICE_SVT,
+        'avm': AVIF_CODEC_CHOICE_AVM,
     }[codec]

++++++ integrate.patch ++++++
>From e9b5a984b72c9d4e14f9d37ec99389d25645c7fb Mon Sep 17 00:00:00 2001
From: Christoph Gohlke <cgoh...@cgohlke.com>
Date: Sun, 3 Sep 2023 09:37:58 -0700
Subject: [PATCH] Update imagecodecs/imagecodecs.py

---
 imagecodecs/imagecodecs.py | 98 ++++++++++++++++++++++++--------------
 1 file changed, 62 insertions(+), 36 deletions(-)

Index: imagecodecs-2023.3.16/imagecodecs/imagecodecs.py
===================================================================
--- imagecodecs-2023.3.16.orig/imagecodecs/imagecodecs.py
+++ imagecodecs-2023.3.16/imagecodecs/imagecodecs.py
@@ -483,9 +483,6 @@ import sys
 import io
 import importlib
 import threading
-
-import numpy
-
 from typing import TYPE_CHECKING
 
 if TYPE_CHECKING:
@@ -496,6 +493,8 @@ if TYPE_CHECKING:
 
     from numpy.typing import ArrayLike, NDArray
 
+import numpy
+
 # map extension module names to attribute names
 _MODULES: dict[str, list[str]] = {
     '': [
@@ -834,6 +833,14 @@ _MODULES: dict[str, list[str]] = {
         'pglz_check',
         'pglz_version',
     ],
+    '_png': [
+        'PNG',
+        'PngError',
+        'png_encode',
+        'png_decode',
+        'png_check',
+        'png_version',
+    ],
     '_qoi': [
         'QOI',
         'QoiError',
@@ -842,13 +849,13 @@ _MODULES: dict[str, list[str]] = {
         'qoi_check',
         'qoi_version',
     ],
-    '_png': [
-        'PNG',
-        'PngError',
-        'png_encode',
-        'png_decode',
-        'png_check',
-        'png_version',
+    '_quantize': [
+        'QUANTIZE',
+        'QuantizeError',
+        'quantize_encode',
+        'quantize_decode',
+        'quantize_check',
+        'quantize_version',
     ],
     '_rgbe': [
         'RGBE',
@@ -1144,6 +1151,8 @@ def _stub(name: str, module: ModuleType
         return StubError
 
     class StubType(type):
+        """Stub type metaclass."""
+
         def __getattr__(cls, arg: str, /) -> Any:
             raise DelayedImportError(name)
 

++++++ libavif.patch ++++++
>From d04112759c48772c4d46a2dfa4f4c6a76e23c9a9 Mon Sep 17 00:00:00 2001
From: Christoph Gohlke <cgoh...@cgohlke.com>
Date: Sat, 2 Sep 2023 16:52:31 -0700
Subject: [PATCH] Update imagecodecs/libavif.pxd

---
 imagecodecs/libavif.pxd | 159 ++++++++++++++++++++++++++--------------
 1 file changed, 102 insertions(+), 57 deletions(-)

diff --git a/imagecodecs/libavif.pxd b/imagecodecs/libavif.pxd
index 9d17cc5..c9db115 100644
--- a/imagecodecs/libavif.pxd
+++ b/imagecodecs/libavif.pxd
@@ -1,10 +1,10 @@
 # imagecodecs/libavif.pxd
 # cython: language_level = 3
 
-# Cython declarations for the `libavif 0.11.1` library.
+# Cython declarations for the `libavif 1.0.1` library.
 # https://github.com/AOMediaCodec/libavif
 
-from libc.stdint cimport uint8_t, uint32_t, uint64_t
+from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, int32_t
 
 cdef extern from 'avif/avif.h':
 
@@ -24,6 +24,11 @@ cdef extern from 'avif/avif.h':
     int AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE
     int AVIF_DEFAULT_IMAGE_COUNT_LIMIT
 
+    int AVIF_QUALITY_DEFAULT
+    int AVIF_QUALITY_LOSSLESS
+    int AVIF_QUALITY_WORST
+    int AVIF_QUALITY_BEST
+
     int AVIF_QUANTIZER_LOSSLESS
     int AVIF_QUANTIZER_BEST_QUALITY
     int AVIF_QUANTIZER_WORST_QUALITY
@@ -34,6 +39,11 @@ cdef extern from 'avif/avif.h':
     int AVIF_SPEED_SLOWEST
     int AVIF_SPEED_FASTEST
 
+    int AVIF_REPETITION_COUNT_INFINITE
+    int AVIF_REPETITION_COUNT_UNKNOWN
+
+    int AVIF_MAX_AV1_LAYER_COUNT
+
     ctypedef enum avifPlanesFlag:
         AVIF_PLANES_YUV
         AVIF_PLANES_A
@@ -42,12 +52,10 @@ cdef extern from 'avif/avif.h':
     ctypedef uint32_t avifPlanesFlags
 
     ctypedef enum avifChannelIndex:
-        AVIF_CHAN_R
-        AVIF_CHAN_G
-        AVIF_CHAN_B
         AVIF_CHAN_Y
         AVIF_CHAN_U
         AVIF_CHAN_V
+        AVIF_CHAN_A
 
     # Version
 
@@ -82,7 +90,7 @@ cdef extern from 'avif/avif.h':
         AVIF_RESULT_ENCODE_COLOR_FAILED
         AVIF_RESULT_ENCODE_ALPHA_FAILED
         AVIF_RESULT_BMFF_PARSE_FAILED
-        AVIF_RESULT_NO_AV1_ITEMS_FOUND
+        AVIF_RESULT_MISSING_IMAGE_ITEM
         AVIF_RESULT_DECODE_COLOR_FAILED
         AVIF_RESULT_DECODE_ALPHA_FAILED
         AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH
@@ -101,6 +109,7 @@ cdef extern from 'avif/avif.h':
         AVIF_RESULT_OUT_OF_MEMORY
         AVIF_RESULT_CANNOT_CHANGE_SETTING
         AVIF_RESULT_INCOMPATIBLE_IMAGE
+        AVIF_RESULT_NO_AV1_ITEMS_FOUND
 
     const char* avifResultToString(
         avifResult result
@@ -118,12 +127,12 @@ cdef extern from 'avif/avif.h':
 
     # int AVIF_DATA_EMPTY { NULL, 0 }
 
-    void avifRWDataRealloc(
+    avifResult avifRWDataRealloc(
         avifRWData* raw,
         size_t newSize
     ) nogil
 
-    void avifRWDataSet(
+    avifResult avifRWDataSet(
         avifRWData* raw,
         const uint8_t* data,
         size_t len
@@ -133,6 +142,20 @@ cdef extern from 'avif/avif.h':
         avifRWData* raw
     ) nogil
 
+    # Metadata
+
+    avifResult avifGetExifTiffHeaderOffset(
+        const uint8_t* exif,
+        size_t exifSize,
+        size_t* offset
+    ) nogil
+
+    avifResult avifGetExifOrientationOffset(
+        const uint8_t* exif,
+        size_t exifSize,
+        size_t* offset
+    ) nogil
+
     # avifPixelFormat
 
     ctypedef enum avifPixelFormat:
@@ -218,6 +241,15 @@ cdef extern from 'avif/avif.h':
         AVIF_TRANSFER_CHARACTERISTICS_SMPTE428
         AVIF_TRANSFER_CHARACTERISTICS_HLG
 
+    avifResult avifTransferCharacteristicsGetGamma(
+        avifTransferCharacteristics atc,
+        float* gamma
+    ) nogil
+
+    avifTransferCharacteristics avifTransferCharacteristicsFindByGamma(
+        float gamma
+    ) nogil
+
     ctypedef enum avifMatrixCoefficients:
         AVIF_MATRIX_COEFFICIENTS_IDENTITY
         AVIF_MATRIX_COEFFICIENTS_BT709
@@ -233,12 +265,21 @@ cdef extern from 'avif/avif.h':
         AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL
         AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL
         AVIF_MATRIX_COEFFICIENTS_ICTCP
+        AVIF_MATRIX_COEFFICIENTS_YCGCO_RE
+        AVIF_MATRIX_COEFFICIENTS_YCGCO_RO
+        AVIF_MATRIX_COEFFICIENTS_LAST
 
     ctypedef struct avifDiagnostics:
         char error[256]  # [AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE]
 
     void avifDiagnosticsClearError(avifDiagnostics* diag) nogil
 
+    # Fraction utility
+
+    ctypedef struct avifFraction:
+        int32_t n
+        int32_t d
+
     # Optional transformation structs
 
     ctypedef enum avifTransformFlag:
@@ -268,7 +309,7 @@ cdef extern from 'avif/avif.h':
         uint8_t angle
 
     ctypedef struct avifImageMirror:
-        uint8_t mode
+        uint8_t axis
 
     ctypedef struct avifCropRect:
         uint32_t x
@@ -294,6 +335,10 @@ cdef extern from 'avif/avif.h':
         avifDiagnostics* diag
     ) nogil
 
+    ctypedef struct avifContentLightLevelInformationBox:
+        uint16_t maxCLL
+        uint16_t maxPALL
+
     # avifImage
 
     ctypedef struct avifImage:
@@ -314,6 +359,7 @@ cdef extern from 'avif/avif.h':
         avifColorPrimaries colorPrimaries
         avifTransferCharacteristics transferCharacteristics
         avifMatrixCoefficients matrixCoefficients
+        avifContentLightLevelInformationBox clli
         avifTransformFlags transformFlags
         avifPixelAspectRatioBox pasp
         avifCleanApertureBox clap
@@ -347,19 +393,19 @@ cdef extern from 'avif/avif.h':
         avifImage* image
     ) nogil
 
-    void avifImageSetProfileICC(
+    avifResult avifImageSetProfileICC(
         avifImage* image,
         const uint8_t* icc,
         size_t iccSize
     ) nogil
 
-    void avifImageSetMetadataExif(
+    avifResult avifImageSetMetadataExif(
         avifImage* image,
         const uint8_t* exif,
         size_t exifSize
     ) nogil
 
-    void avifImageSetMetadataXMP(
+    avifResult avifImageSetMetadataXMP(
         avifImage* image,
         const uint8_t* xmp,
         size_t xmpSize
@@ -422,8 +468,9 @@ cdef extern from 'avif/avif.h':
         avifChromaDownsampling chromaDownsampling
         avifBool avoidLibYUV
         avifBool ignoreAlpha
-        avifBool isFloat
         avifBool alphaPremultiplied
+        avifBool isFloat
+        int maxThreads
         uint8_t* pixels
         uint32_t rowBytes
 
@@ -438,7 +485,7 @@ cdef extern from 'avif/avif.h':
 
     # Convenience functions
 
-    void avifRGBImageAllocatePixels(
+    avifResult avifRGBImageAllocatePixels(
         avifRGBImage* rgb
     ) nogil
 
@@ -471,62 +518,25 @@ cdef extern from 'avif/avif.h':
     # YUV Utils
 
     int avifFullToLimitedY(
-        int depth,
+        uint32_t depth,
         int v
     ) nogil
 
     int avifFullToLimitedUV(
-        int depth,
+        uint32_t depth,
         int v
     ) nogil
 
     int avifLimitedToFullY(
-        int depth,
+        uint32_t depth,
         int v
     ) nogil
 
     int avifLimitedToFullUV(
-        int depth,
+        uint32_t depth,
         int v
     ) nogil
 
-    # removed in v0.9
-    #
-    # ctypedef enum avifReformatMode:
-    #     AVIF_REFORMAT_MODE_YUV_COEFFICIENTS
-    #     AVIF_REFORMAT_MODE_IDENTITY
-
-    # ctypedef struct avifReformatState:
-    #     float kr
-    #     float kg
-    #     float kb
-    #     uint32_t yuvChannelBytes
-    #     uint32_t rgbChannelBytes
-    #     uint32_t rgbChannelCount
-    #     uint32_t rgbPixelBytes
-    #     uint32_t rgbOffsetBytesR
-    #     uint32_t rgbOffsetBytesG
-    #     uint32_t rgbOffsetBytesB
-    #     uint32_t rgbOffsetBytesA
-    #     uint32_t yuvDepth
-    #     uint32_t rgbDepth
-    #     avifRange yuvRange
-    #     int yuvMaxChannel
-    #     int rgbMaxChannel
-    #     float yuvMaxChannelF
-    #     float rgbMaxChannelF
-    #     int uvBias
-    #     avifPixelFormatInfo formatInfo
-    #     float unormFloatTableY[1 << 12]
-    #     float unormFloatTableUV[1 << 12]
-    #     avifReformatMode mode
-
-    # avifBool avifPrepareReformatState(
-    #     const avifImage* image,
-    #     const avifRGBImage* rgb,
-    #     avifReformatState* state
-    # ) nogil
-
     # Codec selection
 
     ctypedef enum avifCodecChoice:
@@ -536,6 +546,7 @@ cdef extern from 'avif/avif.h':
         AVIF_CODEC_CHOICE_LIBGAV1
         AVIF_CODEC_CHOICE_RAV1E
         AVIF_CODEC_CHOICE_SVT
+        AVIF_CODEC_CHOICE_AVM
 
     ctypedef enum avifCodecFlag:
         AVIF_CODEC_FLAG_CAN_DECODE
@@ -657,6 +668,7 @@ cdef extern from 'avif/avif.h':
         uint64_t timescale
         double duration
         uint64_t durationInTimescales
+        int repetitionCount
         avifBool alphaPresent
         avifIOStats ioStats
         avifDiagnostics diag
@@ -753,12 +765,20 @@ cdef extern from 'avif/avif.h':
     struct avifCodecSpecificOptions:
         pass
 
+    ctypedef struct avifScalingMode:
+        avifFraction horizontal
+        avifFraction vertical
+
     ctypedef struct avifEncoder:
         avifCodecChoice codecChoice
         int maxThreads
         int speed
         int keyframeInterval
         uint64_t timescale
+        int repetitionCount
+        uint32_t extraLayerCount
+        int quality
+        int qualityAlpha
         int minQuantizer
         int maxQuantizer
         int minQuantizerAlpha
@@ -766,6 +786,7 @@ cdef extern from 'avif/avif.h':
         int tileRowsLog2
         int tileColsLog2
         avifBool autoTiling
+        avifScalingMode scalingMode
         avifIOStats ioStats
         avifDiagnostics diag
         avifEncoderData* data
@@ -801,7 +822,7 @@ cdef extern from 'avif/avif.h':
         avifEncoder* encoder,
         uint32_t gridCols,
         uint32_t gridRows,
-        const avifImage* const *cellImages,
+        const avifImage* const* cellImages,
         avifAddImageFlags addImageFlags
     )
 
@@ -810,7 +831,7 @@ cdef extern from 'avif/avif.h':
         avifRWData* output
     ) nogil
 
-    void avifEncoderSetCodecSpecificOption(
+    avifResult avifEncoderSetCodecSpecificOption(
         avifEncoder* encoder,
         const char* key,
         const char* value
@@ -822,6 +843,30 @@ cdef extern from 'avif/avif.h':
         const avifImage* image
     ) nogil
 
+    avifBool avifImageIsOpaque(
+        const avifImage* image
+    ) nogil
+
+    uint8_t* avifImagePlane(
+        const avifImage* image,
+        int channel
+    ) nogil
+
+    uint32_t avifImagePlaneRowBytes(
+        const avifImage* image,
+        int channel
+    ) nogil
+
+    uint32_t avifImagePlaneWidth(
+        const avifImage* image,
+        int channel
+    ) nogil
+
+    uint32_t avifImagePlaneHeight(
+        const avifImage* image,
+        int channel
+    ) nogil
+
     avifBool avifPeekCompatibleFileType(
         const avifROData* input
     ) nogil

++++++ quantize.patch ++++++
>From 93d1f751436e357d73eb6fdebc2af833059d9ea9 Mon Sep 17 00:00:00 2001
From: Christoph Gohlke <cgoh...@cgohlke.com>
Date: Sat, 2 Sep 2023 22:41:40 -0700
Subject: [PATCH] Add imagecodecs/_quantize.pyx

---
 imagecodecs/_quantize.pyx | 217 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 217 insertions(+)
 create mode 100644 imagecodecs/_quantize.pyx

diff --git a/imagecodecs/_quantize.pyx b/imagecodecs/_quantize.pyx
new file mode 100644
index 0000000..912eb32
--- /dev/null
+++ b/imagecodecs/_quantize.pyx
@@ -0,0 +1,217 @@
+# imagecodecs/_quantize.pyx
+# distutils: language = c
+# cython: language_level = 3
+# cython: boundscheck=False
+# cython: wraparound=False
+# cython: cdivision=True
+# cython: nonecheck=False
+
+# Copyright (c) 2023, Christoph Gohlke
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+"""Quantize codec for the imagecodecs package."""
+
+__version__ = '2023.9.4'
+
+include '_shared.pxi'
+
+from nc4var cimport *
+
+from libc.math cimport log10, log2, floor, ceil, round, pow
+
+
+class QUANTIZE:
+    """Quantize codec constants."""
+
+    available = True
+
+    class MODE(enum.IntEnum):
+        """Quantize mode."""
+
+        NOQUANTIZE = NC_NOQUANTIZE
+        BITGROOM = NC_QUANTIZE_BITGROOM
+        GRANULARBR = NC_QUANTIZE_GRANULARBR
+        BITROUND = NC_QUANTIZE_BITROUND
+        SCALE = 100
+
+
+class QuantizeError(RuntimeError):
+    """Quantize codec exceptions."""
+
+
+def quantize_version():
+    """Return nc4var library version string."""
+    return f'nc4var {NC_VERSION_MAJOR}.{NC_VERSION_MINOR}.{NC_VERSION_PATCH}'
+
+
+def quantize_encode(data, mode, int nsd, out=None):
+    """Return quantized floating point data.
+
+    ``nsd`` ("number of significant digits") is interpreted differently
+    by the modes:
+
+    - BitGroom and Scale: number of significant decimal digits
+    - BitRound: number of significant binary digits (bits).
+    - Granular BitRound: ?
+
+    """
+    cdef:
+        numpy.ndarray src
+        numpy.ndarray dst
+        size_t src_size
+        nc_type src_type
+        int ret
+        int range_error = 0
+        int strict_nc3 = 0
+        int quantize_mode = NC_NOQUANTIZE
+        # uint64_t fill_value = 0
+
+    data = numpy.ascontiguousarray(data)
+    src = data
+    src_size = <size_t>src.size
+
+    if src.dtype.kind != b'f' and src.itemsize not in {4, 8}:
+        raise ValueError('not a floating-point array')
+
+    if src.itemsize == 4:
+        src_type = NC_FLOAT
+    else:
+        src_type = NC_DOUBLE
+
+    if mode in {
+        NC_NOQUANTIZE,
+        NC_QUANTIZE_BITGROOM,
+        NC_QUANTIZE_GRANULARBR,
+        NC_QUANTIZE_BITROUND,
+        100
+    }:
+        quantize_mode = mode
+    elif mode == 'bitround':
+        quantize_mode = NC_QUANTIZE_BITROUND
+    elif mode == 'bitgroom':
+        quantize_mode = NC_QUANTIZE_BITGROOM
+    elif mode in {'granularbr', 'gbr'}:
+        quantize_mode = NC_QUANTIZE_GRANULARBR
+    elif mode == 'scale':
+        quantize_mode = 100
+    elif mode == 'noquantize':
+        quantize_mode = NC_NOQUANTIZE
+    else:
+        raise ValueError(f'invalid quantize mode {mode!r}')
+
+    if not 0 <= nsd < 64:
+        raise ValueError(f'invalid number of significant digits {nsd!r}')
+
+    out = _create_array(out, data.shape, data.dtype)
+    dst = out
+
+    with nogil:
+        if quantize_mode == 100:
+            if src_type == NC_FLOAT:
+                quantize_scale_f(
+                    <float*>src.data,
+                    <float*>dst.data,
+                    src_size,
+                    nsd
+                )
+            else:
+                quantize_scale_d(
+                    <double*>src.data,
+                    <double*>dst.data,
+                    src_size,
+                    nsd
+                )
+        else:
+            ret = nc4_convert_type(
+                <const void *>src.data,
+                <void *>dst.data,
+                src_type,
+                src_type,
+                src_size,
+                &range_error,
+                NULL,  # fill_value
+                1,  # strict_nc3
+                quantize_mode,
+                nsd
+            )
+        if ret < 0:
+            raise QuantizeError(f'nc4_convert_type returnd {ret!r}')
+
+    return out
+
+
+def quantize_decode(data, mode, nsd, out=None):
+    """Return de-quantized data. Raise QuantizeError if lossy."""
+    if mode != NC_NOQUANTIZE:
+        raise QuantizeError(f'Quantize mode {mode} is lossy.')
+    return data
+
+###############################################################################
+
+# quantize_scale
+# Data is quantized using round(scale*data)/scale, where scale is 2**bits,
+# and bits is determined from the nsd. For example, if nsd=1, bits will be 4.
+# https://github.com/Blosc/bcolz utils.py
+
+cdef void quantize_scale_f(
+    const float* data,
+    float* out,
+    ssize_t size,
+    int nsb
+) nogil:
+    cdef:
+        float scale
+        double exp
+        ssize_t i
+
+    exp = log10(pow(10.0, -nsb))
+    exp = floor(exp) if exp < 0.0 else ceil(exp)
+    scale = <float> pow(2.0, ceil(log2(pow(10.0, -exp))))
+
+    for i in range(size):
+        out[i] = round(data[i] * scale) / scale
+
+
+cdef void quantize_scale_d(
+    const double* data,
+    double* out,
+    ssize_t size,
+    int nsb
+) nogil:
+    cdef:
+        double scale
+        double exp
+        ssize_t i
+
+    exp = log10(pow(10.0, -nsb))
+    exp = floor(exp) if exp < 0.0 else ceil(exp)
+    scale = <double> pow(2.0, ceil(log2(pow(10.0, -exp))))
+
+    for i in range(size):
+        out[i] = round(data[i] * scale) / scale

++++++ tests.patch ++++++
>From 0030b7b74fc17ceb356d1f67633ba1734108dac9 Mon Sep 17 00:00:00 2001
From: Christoph Gohlke <cgoh...@cgohlke.com>
Date: Sun, 3 Sep 2023 09:36:27 -0700
Subject: [PATCH] Update tests/test_imagecodecs.py

---
 tests/test_imagecodecs.py | 131 ++++++++++++++++++++++++++++++--------
 1 file changed, 106 insertions(+), 25 deletions(-)

Index: imagecodecs-2023.3.16/tests/test_imagecodecs.py
===================================================================
--- imagecodecs-2023.3.16.orig/tests/test_imagecodecs.py
+++ imagecodecs-2023.3.16/tests/test_imagecodecs.py
@@ -79,6 +79,7 @@ except ImportError as exc:
 
 try:
     import zarr
+
     from imagecodecs import numcodecs
 except ImportError:
     SKIP_NUMCODECS = True
@@ -172,7 +173,7 @@ def test_dependency_exist(name):
     if SKIP_NUMCODECS and IS_PYPY:
         mayfail = True
     elif name in ('blosc', 'blosc2', 'snappy'):
-        if IS_PYPY or sys.version_info[1] >= 10:
+        if IS_PYPY or not IS_CG:
             mayfail = True
     try:
         importlib.import_module(name)
@@ -194,7 +195,7 @@ def test_version_functions():
 def test_stubs():
     """Test stub attributes for non-existing extension."""
     with pytest.raises(AttributeError):
-        imagecodecs._STUB
+        assert imagecodecs._STUB
     _add_codec('_stub')
     assert not imagecodecs._STUB  # typing: ignore
     assert not imagecodecs._STUB.available
@@ -873,6 +874,74 @@ def test_lzw_msb():
 
 
 @pytest.mark.skipif(
+    not imagecodecs.QUANTIZE.available, reason='QUANTIZE missing'
+)
+@pytest.mark.parametrize(
+    'mode', ['bitgroom', 'granularbr', 'bitround', 'scale']
+)
+@pytest.mark.parametrize('dtype', ['f4', 'f8'])
+def test_quantize_roundtrip(mode, dtype):
+    """Test quantize roundtrips."""
+    nsd = 12
+    atol = 0.006
+    if mode == 'bitgroom':
+        nsd = (nsd - 1) // 3  # bits = math.ceil(nsd * 3.32) + 1
+    if dtype == 'f4':
+        nsd //= 2
+        atol = 0.5
+    data = numpy.linspace(-2.1, 31.4, 51, dtype=dtype).reshape((3, 17))
+    encoded = imagecodecs.quantize_encode(data, mode, nsd)
+    out = data.copy()
+    imagecodecs.quantize_encode(data, mode, nsd, out=out)
+    assert_array_equal(out, encoded)
+    assert_allclose(data, encoded, atol=atol)
+
+
+@pytest.mark.skipif(
+    SKIP_NUMCODECS or not imagecodecs.QUANTIZE.available,
+    reason='QUANTIZE missing',
+)
+@pytest.mark.parametrize('nsd', [1, 4])
+@pytest.mark.parametrize('dtype', ['f4', 'f8'])
+def test_quantize_bitround(dtype, nsd):
+    """Test BitRound quantize against numcodecs."""
+    from numcodecs import BitRound
+
+    from imagecodecs.numcodecs import Quantize
+
+    # TODO: 31.4 fails
+    data = numpy.linspace(-2.1, 31.5, 51, dtype=dtype).reshape((3, 17))
+    encoded = Quantize(
+        mode=imagecodecs.QUANTIZE.MODE.BITROUND,
+        nsd=nsd,
+    ).encode(data)
+    nc = BitRound(keepbits=nsd)
+    encoded2 = nc.decode(nc.encode(data))
+    assert_array_equal(encoded, encoded2)
+
+
+@pytest.mark.skipif(
+    SKIP_NUMCODECS or not imagecodecs.QUANTIZE.available,
+    reason='QUANTIZE missing',
+)
+@pytest.mark.parametrize('nsd', [1, 4])
+@pytest.mark.parametrize('dtype', ['f4', 'f8'])
+def test_quantize_scale(dtype, nsd):
+    """Test Scale quantize against numcodecs."""
+    from numcodecs import Quantize as Quantize2
+
+    from imagecodecs.numcodecs import Quantize
+
+    data = numpy.linspace(-2.1, 31.4, 51, dtype=dtype).reshape((3, 17))
+    encoded = Quantize(
+        mode=imagecodecs.QUANTIZE.MODE.SCALE,
+        nsd=nsd,
+    ).encode(data)
+    encoded2 = Quantize2(digits=nsd, dtype=dtype).encode(data)
+    assert_array_equal(encoded, encoded2)
+
+
+@pytest.mark.skipif(
     not (imagecodecs.LZW.available and imagecodecs.DELTA.available),
     reason='skip',
 )
@@ -1818,7 +1896,7 @@ def test_rgbe_roundtrip():
 @pytest.mark.skipif(not imagecodecs.CMS.available, reason='cms missing')
 def test_cms_profile():
     """Test cms_profile function."""
-    from imagecodecs import cms_profile, cms_profile_validate, CmsError
+    from imagecodecs import CmsError, cms_profile, cms_profile_validate
 
     with pytest.raises(CmsError):
         cms_profile_validate(b'12345')
@@ -1934,7 +2012,7 @@ def test_cms_profile():
 @pytest.mark.skipif(not imagecodecs.CMS.available, reason='cms missing')
 def test_cms_output_shape():
     """Test _cms_output_shape function."""
-    from imagecodecs._cms import _cms_output_shape, _cms_format
+    from imagecodecs._cms import _cms_format, _cms_output_shape
 
     for args, colorspace, planar, expected in (
         (((6, 7), 'u1', 'gray'), 'gray', 0, (6, 7)),
@@ -2086,7 +2164,7 @@ def test_cms_format():
 @pytest.mark.parametrize('out', [None, True])
 def test_cms_identity_transforms(dtype, outdtype, planar, outplanar, out):
     """Test CMS identity transforms."""
-    from imagecodecs import cms_transform, cms_profile
+    from imagecodecs import cms_profile, cms_transform
 
     shape = (3, 256, 253) if planar else (256, 253, 3)
     dtype = numpy.dtype(dtype)
@@ -2480,7 +2558,10 @@ def test_avif_strict_disabled():
 
 
 @pytest.mark.skipif(not IS_CG, reason='avif missing')
-@pytest.mark.parametrize('codec', ['auto', 'aom', 'rav1e', 'svt'])  # 'libgav1'
+@pytest.mark.parametrize(
+    'codec',
+    ['auto', 'aom', 'rav1e', 'svt'],  # 'libgav1', 'avm'
+)
 def test_avif_encoder(codec):
     """Test various AVIF encoder codecs."""
     data = numpy.load(datafiles('rgb.u1.npy'))
@@ -2490,9 +2571,9 @@ def test_avif_encoder(codec):
     else:
         pixelformat = None
     encoded = imagecodecs.avif_encode(
-        data, level=6, codec=codec, pixelformat=pixelformat
+        data, level=95, codec=codec, pixelformat=pixelformat, numthreads=2
     )
-    decoded = imagecodecs.avif_decode(encoded)
+    decoded = imagecodecs.avif_decode(encoded, numthreads=2)
     assert_allclose(decoded, data, atol=5, rtol=0)
 
 
@@ -3353,6 +3434,8 @@ def test_image_roundtrips(codec, dtype,
                     data, bitspersample=12, *args, **kwargs
                 )
 
+        if level:
+            level += 95
         atol = 10
     elif codec == 'heif':
         if not imagecodecs.HEIF.available:
@@ -3431,13 +3514,8 @@ def test_image_roundtrips(codec, dtype,
         if level < 100:
             atol *= 4
         assert_allclose(data, decoded, atol=atol)
-    elif codec == 'avif' and level == 5:
-        if dtype.itemsize > 1:
-            # TODO: bug in libavif?
-            pytest.xfail('why does this fail?')
-            atol = 32
-        else:
-            atol = 6
+    elif codec == 'avif' and level == 94:
+        atol = 38 if dtype.itemsize > 1 else 6
         assert_allclose(data, decoded, atol=atol)
     else:
         assert_array_equal(data, decoded, verbose=True)
@@ -3847,7 +3926,7 @@ def test_numcodecs(codec, photometric):
         if photometric != 'rgb':
             pytest.xfail('AVIF does not support grayscale')
         compressor = numcodecs.Avif(
-            level=0,
+            level=100,
             speed=None,
             tilelog2=None,
             bitspersample=None,

Reply via email to