opencodecs 0.7.0__tar.gz → 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. {opencodecs-0.7.0 → opencodecs-0.7.1}/CHANGES.rst +79 -0
  2. {opencodecs-0.7.0/src/opencodecs.egg-info → opencodecs-0.7.1}/PKG-INFO +13 -1
  3. {opencodecs-0.7.0 → opencodecs-0.7.1}/pyproject.toml +16 -2
  4. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/__init__.py +1 -1
  5. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_reader.py +4 -0
  6. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_writer.py +68 -6
  7. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jxl_codec.py +17 -1
  8. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_omezarr.py +4 -1
  9. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_omezarr_writer.py +19 -7
  10. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/segment_compression.py +6 -0
  11. {opencodecs-0.7.0 → opencodecs-0.7.1/src/opencodecs.egg-info}/PKG-INFO +13 -1
  12. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/SOURCES.txt +2 -0
  13. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/requires.txt +12 -0
  14. opencodecs-0.7.1/tests/test_czi_foreign_readers.py +163 -0
  15. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_pread_indices.py +3 -1
  16. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_read_dispatch.py +7 -1
  17. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_recompress.py +102 -1
  18. opencodecs-0.7.1/tests/test_foreign_readers.py +653 -0
  19. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_segment_encoder_binding.py +3 -1
  20. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/VENDOR.toml +0 -0
  21. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/LICENSE +0 -0
  22. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/README.md +0 -0
  23. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/bcdec.h +0 -0
  24. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/LICENSE +0 -0
  25. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/README.rst +0 -0
  26. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle.c +0 -0
  27. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle.h +0 -0
  28. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_core.c +0 -0
  29. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_core.h +0 -0
  30. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_internals.h +0 -0
  31. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/iochain.c +0 -0
  32. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/iochain.h +0 -0
  33. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/License.txt +0 -0
  34. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hcompress.c +0 -0
  35. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hdecompress.c +0 -0
  36. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hdecompress_api.h +0 -0
  37. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fitsio2.h +0 -0
  38. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/pliocomp.c +0 -0
  39. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/ricecomp.c +0 -0
  40. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/ricecomp.h +0 -0
  41. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/LICENSE +0 -0
  42. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/spng.c +0 -0
  43. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/spng.h +0 -0
  44. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/LICENSE +0 -0
  45. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/oc_eer.c +0 -0
  46. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/oc_eer.h +0 -0
  47. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/LICENSE +0 -0
  48. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/oc_giflzw.c +0 -0
  49. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/oc_giflzw.h +0 -0
  50. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/LICENSE +0 -0
  51. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/oc_tifflzw.c +0 -0
  52. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/oc_tifflzw.h +0 -0
  53. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/qoi/LICENSE +0 -0
  54. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/qoi/qoi.h +0 -0
  55. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.c +0 -0
  56. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.h +0 -0
  57. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.txt +0 -0
  58. {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/vendor_hashes.json +0 -0
  59. {opencodecs-0.7.0 → opencodecs-0.7.1}/LICENSE +0 -0
  60. {opencodecs-0.7.0 → opencodecs-0.7.1}/MANIFEST.in +0 -0
  61. {opencodecs-0.7.0 → opencodecs-0.7.1}/README.md +0 -0
  62. {opencodecs-0.7.0 → opencodecs-0.7.1}/THIRD-PARTY.md +0 -0
  63. {opencodecs-0.7.0 → opencodecs-0.7.1}/bench/build_libjxl.sh +0 -0
  64. {opencodecs-0.7.0 → opencodecs-0.7.1}/patches/libjxl/README.md +0 -0
  65. {opencodecs-0.7.0 → opencodecs-0.7.1}/setup.cfg +0 -0
  66. {opencodecs-0.7.0 → opencodecs-0.7.1}/setup.py +0 -0
  67. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_aec_codec.py +0 -0
  68. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_avif_codec.py +0 -0
  69. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_avif_heif_params.py +0 -0
  70. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_b2nd_codec.py +0 -0
  71. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bcn_codec.py +0 -0
  72. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bitshuffle_codec.py +0 -0
  73. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_blosc2_codec.py +0 -0
  74. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bmp_codec.py +0 -0
  75. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_brotli_codec.py +0 -0
  76. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_brunsli_codec.py +0 -0
  77. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_byteshuffle_codec.py +0 -0
  78. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bz2_codec.py +0 -0
  79. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_cms_codec.py +0 -0
  80. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_codec.py +0 -0
  81. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_deflate_codec.py +0 -0
  82. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom.py +0 -0
  83. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom_codec.py +0 -0
  84. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom_pyramid.py +0 -0
  85. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicomrle_codec.py +0 -0
  86. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicomweb.py +0 -0
  87. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dm.py +0 -0
  88. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dm_codec.py +0 -0
  89. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_eer_reader.py +0 -0
  90. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_emd.py +0 -0
  91. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_emd_codec.py +0 -0
  92. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ets.py +0 -0
  93. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_filter_args.py +0 -0
  94. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits.py +0 -0
  95. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits_codec.py +0 -0
  96. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits_compressed.py +0 -0
  97. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_gif_codec.py +0 -0
  98. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_gzip_codec.py +0 -0
  99. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_h5_common.py +0 -0
  100. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_hdf5_codec.py +0 -0
  101. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_hdf5_http.py +0 -0
  102. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_heif_codec.py +0 -0
  103. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_htj2k_codec.py +0 -0
  104. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_htj2k_pyramid.py +0 -0
  105. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_imaris.py +0 -0
  106. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg2k_codec.py +0 -0
  107. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg2k_pyramid.py +0 -0
  108. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg_codec.py +0 -0
  109. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg_pyramid.py +0 -0
  110. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpegls_codec.py +0 -0
  111. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lerc_codec.py +0 -0
  112. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lif_codec.py +0 -0
  113. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lif_native.py +0 -0
  114. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lz4_codec.py +0 -0
  115. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lzma_codec.py +0 -0
  116. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mozjpeg_codec.py +0 -0
  117. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mozjpeg_pyramid.py +0 -0
  118. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc.py +0 -0
  119. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc_codec.py +0 -0
  120. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc_writer.py +0 -0
  121. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_n5.py +0 -0
  122. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nd2_codec.py +0 -0
  123. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nd2_native.py +0 -0
  124. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff.py +0 -0
  125. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff_pyramid.py +0 -0
  126. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff_writer.py +0 -0
  127. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti.py +0 -0
  128. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti_codec.py +0 -0
  129. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti_writer.py +0 -0
  130. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_none_codec.py +0 -0
  131. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nrrd.py +0 -0
  132. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nrrd_codec.py +0 -0
  133. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_numpy_codec.py +0 -0
  134. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_numpy_reader.py +0 -0
  135. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oib_codec.py +0 -0
  136. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oib_native.py +0 -0
  137. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oir_codec.py +0 -0
  138. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ole2.py +0 -0
  139. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ome_xml.py +0 -0
  140. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_packints_codec.py +0 -0
  141. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_pcodec_codec.py +0 -0
  142. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_png_codec.py +0 -0
  143. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_predictor_codec.py +0 -0
  144. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_pyramid_build.py +0 -0
  145. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_qoi_codec.py +0 -0
  146. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_quantize_codec.py +0 -0
  147. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rcomp_codec.py +0 -0
  148. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rgbe.py +0 -0
  149. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rgbe_codec.py +0 -0
  150. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_scaled_pyramid.py +0 -0
  151. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_snappy_codec.py +0 -0
  152. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_sperr_codec.py +0 -0
  153. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_sz3_codec.py +0 -0
  154. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_codec.py +0 -0
  155. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_http.py +0 -0
  156. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_pyramid.py +0 -0
  157. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_writer.py +0 -0
  158. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ultrahdr_codec.py +0 -0
  159. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_vsi_codec.py +0 -0
  160. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_vsi_pyramid.py +0 -0
  161. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_webp_codec.py +0 -0
  162. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zarr_codecs.py +0 -0
  163. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zfp_codec.py +0 -0
  164. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zstd_codec.py +0 -0
  165. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/__init__.py +0 -0
  166. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_aec.pyx +0 -0
  167. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_avif.pyx +0 -0
  168. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_b2nd.pyx +0 -0
  169. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bcdec.pyx +0 -0
  170. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bitshuffle.pyx +0 -0
  171. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_blosc2.pyx +0 -0
  172. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bmp.pyx +0 -0
  173. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_brotli.pyx +0 -0
  174. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_brunsli.pyx +0 -0
  175. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bytetools.pyx +0 -0
  176. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_charls.pyx +0 -0
  177. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_deflate.pyx +0 -0
  178. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_eer.pyx +0 -0
  179. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_gif.pyx +0 -0
  180. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_hcomp.pyx +0 -0
  181. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_heif.pyx +0 -0
  182. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_isal.pyx +0 -0
  183. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg.pyx +0 -0
  184. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg2k.pyx +0 -0
  185. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg_common.py +0 -0
  186. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpegxr.pyx +0 -0
  187. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jxl.pyx +0 -0
  188. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_lerc.pyx +0 -0
  189. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_lz4.pyx +0 -0
  190. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_mozjpeg.pyx +0 -0
  191. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_ndtiff.pyx +0 -0
  192. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_openjph.pyx +0 -0
  193. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_pcodec.pyx +0 -0
  194. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_plio.pyx +0 -0
  195. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_png.pyx +0 -0
  196. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_qoi.pyx +0 -0
  197. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_rcomp.pyx +0 -0
  198. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_registry.py +0 -0
  199. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_rgbe.pyx +0 -0
  200. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_snappy.pyx +0 -0
  201. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_sperr.pyx +0 -0
  202. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_sz3.pyx +0 -0
  203. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_tiff.pyx +0 -0
  204. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_uhdr.pyx +0 -0
  205. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_webp.pyx +0 -0
  206. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_zfp.pyx +0 -0
  207. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_zstd.pyx +0 -0
  208. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/avif.pxd +0 -0
  209. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd.pxd +0 -0
  210. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd_helpers.c +0 -0
  211. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd_helpers.h +0 -0
  212. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/bcdec.pxd +0 -0
  213. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/bitshuffle.pxd +0 -0
  214. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/blosc2.pxd +0 -0
  215. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/brotli.pxd +0 -0
  216. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/brunsli.pxd +0 -0
  217. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/byteplanes.h +0 -0
  218. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/charls.pxd +0 -0
  219. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/eer.pxd +0 -0
  220. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/giflib.pxd +0 -0
  221. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/heif.pxd +0 -0
  222. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/heif_shim.h +0 -0
  223. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/isal_shim.c +0 -0
  224. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/jpegxr_shim.c +0 -0
  225. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/jpegxr_shim.h +0 -0
  226. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/lerc.pxd +0 -0
  227. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libaec.pxd +0 -0
  228. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libjxl.pxd +0 -0
  229. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libuhdr.pxd +0 -0
  230. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/lz4.pxd +0 -0
  231. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/mozjpeg.pxd +0 -0
  232. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/oc_decoder_vtable.h +0 -0
  233. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjpeg.pxd +0 -0
  234. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph.pxd +0 -0
  235. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph_shim.cpp +0 -0
  236. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph_shim.h +0 -0
  237. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/pcodec.pxd +0 -0
  238. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/qoi.pxd +0 -0
  239. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/rgbe.pxd +0 -0
  240. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/snappy.pxd +0 -0
  241. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/sperr.pxd +0 -0
  242. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/spng.pxd +0 -0
  243. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/sz3c.pxd +0 -0
  244. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/turbojpeg.pxd +0 -0
  245. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp.pxd +0 -0
  246. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp_shim.c +0 -0
  247. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp_shim.h +0 -0
  248. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zfp.pxd +0 -0
  249. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zlib_h.pxd +0 -0
  250. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zstd.pxd +0 -0
  251. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/__init__.py +0 -0
  252. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_io_helpers.py +0 -0
  253. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_optional_backend.py +0 -0
  254. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_range_index.py +0 -0
  255. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_sidecar_header.py +0 -0
  256. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_write_helpers.py +0 -0
  257. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/buffers.py +0 -0
  258. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/checksums.py +0 -0
  259. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/codec.py +0 -0
  260. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/color.py +0 -0
  261. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/errors.py +0 -0
  262. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/io.py +0 -0
  263. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/native_source.py +0 -0
  264. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/parallel.py +0 -0
  265. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/pipeline.py +0 -0
  266. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/pyramid.py +0 -0
  267. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/rows.py +0 -0
  268. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/scratch.py +0 -0
  269. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/streaming.py +0 -0
  270. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/tile_cache.py +0 -0
  271. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/verification.py +0 -0
  272. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/jxl.py +0 -0
  273. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/parallel.py +0 -0
  274. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/tiff_reader.py +0 -0
  275. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/uhdr.py +0 -0
  276. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/zarr.py +0 -0
  277. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/dependency_links.txt +0 -0
  278. {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/top_level.txt +0 -0
  279. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_auto_pyramid.py +0 -0
  280. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_avif_heif_params.py +0 -0
  281. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_avif_sequence.py +0 -0
  282. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_b2nd_file_reader.py +0 -0
  283. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcdec.py +0 -0
  284. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcn_bands.py +0 -0
  285. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcn_rounding.py +0 -0
  286. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bitshuffle_format.py +0 -0
  287. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_blosc2_partial.py +0 -0
  288. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_blosc2_threads.py +0 -0
  289. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bmp_headers.py +0 -0
  290. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bounded_pipeline.py +0 -0
  291. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_brunsli.py +0 -0
  292. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_byteplanes.py +0 -0
  293. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bytestream_standards.py +0 -0
  294. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_capability_manifest.py +0 -0
  295. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_charls.py +0 -0
  296. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_chunked_means_cheap.py +0 -0
  297. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_codec_registry.py +0 -0
  298. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_container_pipeline.py +0 -0
  299. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_container_streaming.py +0 -0
  300. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_core_codec_api.py +0 -0
  301. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus.py +0 -0
  302. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_codec_decode.py +0 -0
  303. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_manifest.py +0 -0
  304. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_native_fixtures.py +0 -0
  305. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_other.py +0 -0
  306. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_png.py +0 -0
  307. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_tiff.py +0 -0
  308. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_vendors.py +0 -0
  309. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_final.py +0 -0
  310. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_finishing.py +0 -0
  311. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_last.py +0 -0
  312. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_more.py +0 -0
  313. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_push.py +0 -0
  314. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_bounded_iteration.py +0 -0
  315. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_direct_decode.py +0 -0
  316. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_fixture.py +0 -0
  317. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_mixed_subblocks.py +0 -0
  318. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_parallel_write.py +0 -0
  319. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_pyramid.py +0 -0
  320. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_read_out.py +0 -0
  321. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_region_index.py +0 -0
  322. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_region_windows.py +0 -0
  323. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_segment_parts.py +0 -0
  324. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_slide_geometry.py +0 -0
  325. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_verification.py +0 -0
  326. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_write_pipeline.py +0 -0
  327. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_writer.py +0 -0
  328. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_writer_shuffle.py +0 -0
  329. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_data_source.py +0 -0
  330. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_decode_releases_gil.py +0 -0
  331. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_deflate.py +0 -0
  332. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_delta_parity.py +0 -0
  333. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom.py +0 -0
  334. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_encodings.py +0 -0
  335. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_parallel.py +0 -0
  336. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_slide_selection.py +0 -0
  337. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_wsi_pyramid.py +0 -0
  338. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicomweb.py +0 -0
  339. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_direct_filter_outputs.py +0 -0
  340. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm.py +0 -0
  341. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm_offsets.py +0 -0
  342. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm_synthetic.py +0 -0
  343. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_edge_cases.py +0 -0
  344. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer.py +0 -0
  345. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer_parallel.py +0 -0
  346. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer_worker_budget.py +0 -0
  347. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_emd.py +0 -0
  348. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_encode_comprehensive.py +0 -0
  349. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_error_handling.py +0 -0
  350. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fd_writes.py +0 -0
  351. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_filter_compat.py +0 -0
  352. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_filter_scratch.py +0 -0
  353. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fits.py +0 -0
  354. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fits_parallel.py +0 -0
  355. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gif.py +0 -0
  356. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gif_compositing.py +0 -0
  357. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gzip_presized.py +0 -0
  358. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_http.py +0 -0
  359. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_local.py +0 -0
  360. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_parallel.py +0 -0
  361. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_remote.py +0 -0
  362. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_heif_avif_features.py +0 -0
  363. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_heif_multi_image.py +0 -0
  364. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_htj2k_compat.py +0 -0
  365. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_htj2k_conformance.py +0 -0
  366. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_byte_savings.py +0 -0
  367. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_prefetch.py +0 -0
  368. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_unified.py +0 -0
  369. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_imaris.py +0 -0
  370. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_incremental_byte_codecs.py +0 -0
  371. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_io_chunked.py +0 -0
  372. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_isal.py +0 -0
  373. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg.py +0 -0
  374. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg2k_compat.py +0 -0
  375. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg2k_region.py +0 -0
  376. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpegxr.py +0 -0
  377. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jxl.py +0 -0
  378. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lerc.py +0 -0
  379. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lif_indexing.py +0 -0
  380. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lif_native.py +0 -0
  381. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_live_archives.py +0 -0
  382. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mozjpeg.py +0 -0
  383. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc.py +0 -0
  384. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc_mrcz_http.py +0 -0
  385. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc_nifti_writers.py +0 -0
  386. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_n5.py +0 -0
  387. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_output_sinks.py +0 -0
  388. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_parity.py +0 -0
  389. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_source_callbacks.py +0 -0
  390. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nd2_native.py +0 -0
  391. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_ndtiff.py +0 -0
  392. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nifti.py +0 -0
  393. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd.py +0 -0
  394. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd_iteration.py +0 -0
  395. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd_slices.py +0 -0
  396. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_numpy_codec_format.py +0 -0
  397. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_offset_readers.py +0 -0
  398. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_oib_indexing.py +0 -0
  399. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_oib_native.py +0 -0
  400. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_ome_xml.py +0 -0
  401. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_omezarr.py +0 -0
  402. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_omezarr_sharded_range.py +0 -0
  403. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_open_pyramid.py +0 -0
  404. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_openjph.py +0 -0
  405. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_optional_backend.py +0 -0
  406. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_owned_color_context.py +0 -0
  407. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_owned_image_decoders.py +0 -0
  408. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_parallel_jxl.py +0 -0
  409. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase4_filters.py +0 -0
  410. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase5_icc.py +0 -0
  411. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase6_codecs.py +0 -0
  412. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase7_cms.py +0 -0
  413. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_budget.py +0 -0
  414. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_catalog.py +0 -0
  415. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_executor.py +0 -0
  416. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_platform_branches.py +0 -0
  417. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_png_row_sessions.py +0 -0
  418. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_png_standard.py +0 -0
  419. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_public_output_buffers.py +0 -0
  420. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pyramid_batched_regions.py +0 -0
  421. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_qoi_standard.py +0 -0
  422. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_range_pipeline_completion.py +0 -0
  423. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rcomp_truncated_input.py +0 -0
  424. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_read_file_into.py +0 -0
  425. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_contract.py +0 -0
  426. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_source_kinds.py +0 -0
  427. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_streaming.py +0 -0
  428. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reference_parity.py +0 -0
  429. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rgbe.py +0 -0
  430. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rgbe_toplevel.py +0 -0
  431. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_scaled_pyramid.py +0 -0
  432. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_shared_io_regressions.py +0 -0
  433. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_sidecar_header.py +0 -0
  434. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_snappy.py +0 -0
  435. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_source_pool_lifetime.py +0 -0
  436. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_streaming_means_streaming.py +0 -0
  437. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_threading_policy.py +0 -0
  438. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tier1_scientific.py +0 -0
  439. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_codec_encode.py +0 -0
  440. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_fused_decode.py +0 -0
  441. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_http.py +0 -0
  442. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_native.py +0 -0
  443. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_parallel_decode.py +0 -0
  444. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_pipeline.py +0 -0
  445. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_predictor2_interop.py +0 -0
  446. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_predictor_loops.py +0 -0
  447. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_pyramid.py +0 -0
  448. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_read_path.py +0 -0
  449. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_reader.py +0 -0
  450. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_spool.py +0 -0
  451. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_stack_read.py +0 -0
  452. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_workers.py +0 -0
  453. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer.py +0 -0
  454. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer_float_predictor.py +0 -0
  455. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer_verification.py +0 -0
  456. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tile_cache.py +0 -0
  457. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_uhdr.py +0 -0
  458. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_unified_api.py +0 -0
  459. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_url_sources.py +0 -0
  460. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vendor_provenance.py +0 -0
  461. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_verified_encoding.py +0 -0
  462. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_version_consistency.py +0 -0
  463. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vsi_oir.py +0 -0
  464. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vsi_pyramid.py +0 -0
  465. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_animation.py +0 -0
  466. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_decode_options.py +0 -0
  467. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_encode_standard.py +0 -0
  468. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_wrapper_streams.py +0 -0
  469. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_contract.py +0 -0
  470. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_pipeline_completion.py +0 -0
  471. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_verification.py +0 -0
  472. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zarr_adapters.py +0 -0
  473. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zarr_region_fast_path.py +0 -0
  474. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zfp_blocks.py +0 -0
  475. {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zstd_owned_output.py +0 -0
@@ -10,6 +10,85 @@ Versions follow the same ``YYYY.M.D`` cadence as upstream when we
10
10
  publish; the entries below cluster work by date rather than by
11
11
  release because most of it has shipped continuously to ``main``.
12
12
 
13
+ 0.7.1 (unreleased)
14
+ ------------------
15
+
16
+ Every encoder is now tested against a decoder that is not ours.
17
+ ``tests/test_foreign_readers.py`` encodes with each of the 46 codecs that can
18
+ encode and decodes with the format's own library where it has a Python
19
+ binding (zstandard, lz4, brotli, python-blosc2, cramjam, lerc, zfpy, pcodec,
20
+ qoi, pillow-heif, the standard library), imagecodecs, and format readers
21
+ (tifffile, mrcfile, nibabel, pydicom, numcodecs, numpy); it also opens the
22
+ files every writer produces (TIFF with each compression, pyramids, NDTiff,
23
+ OME-Zarr, MRC, NIfTI, RGBE, JPEG XL animation, CZI pyramids). A codec that
24
+ gains an encoder without a case fails the suite. CI installs these readers.
25
+ It found:
26
+
27
+ - Fix: the JPEG XL encoder refused ``level``, so ``TiffWriter`` and the NDTiff
28
+ writer raised for JPEG XL with ``compression_level``, which their
29
+ documentation promised to pass through. ``level`` now means what it means
30
+ in ``imagecodecs.jpegxl_encode``: 100 or below is lossy at that quality,
31
+ above 100 lossless. Passing both ``level`` and ``quality`` raises.
32
+ - Fix: OME-Zarr stores written with ``compressor="blosc2"`` opened only in
33
+ opencodecs. No zarr specification defines Blosc2; the one numcodecs plugin
34
+ for it, ocf-blosc2 (Open Climate Fix), registers the id ``blosc2`` with
35
+ the parameters ``cname`` and ``clevel``, where the writer recorded
36
+ ``level``, which that plugin refuses. v2 stores now record ``cname`` and
37
+ ``clevel``, and v3 stores name the codec ``numcodecs.blosc2``, as zarr
38
+ names a numcodecs codec. With ocf-blosc2 installed, zarr opens both and
39
+ reads the pixels exactly. The chunks themselves are unchanged, and stores
40
+ written the old way still read here.
41
+ - Fix: on Windows, which has no ``os.preadv``, the CZI reader maps the file,
42
+ and ``close()`` left the closed mapping in place: the next call raised the
43
+ mapping's ``ValueError: mmap closed or invalid`` instead of the reader's
44
+ own closed-reader ``CziError``. Two tests encoded the positional-read path
45
+ as universal (the payload index is kept on every path; a stored stack is
46
+ one copy out of the mapping on one worker there) and now assert each
47
+ path's own behavior.
48
+
49
+ - Fix: a CZI this library wrote with default metadata could not be opened by
50
+ libCZI, so not by czicompress and not by ZEN. ``CziWriter``'s default
51
+ metadata was a bare ``<Metadata/>``, and libCZI requires the document to be
52
+ rooted at ``ImageDocument``; it refuses anything else outright with
53
+ ``Root-node "ImageDocument" not found``. Every round-trip test in this
54
+ suite passed because every one of them read the file back with our own
55
+ reader, which accepts it. Metadata lacking the root is now wrapped in one,
56
+ so a caller writing a new file cannot produce an unopenable one.
57
+ ``czi_recompress`` passes ``metadata_as_is=True``: it promises the source's
58
+ metadata is carried across unchanged, and rewriting it there would break
59
+ that promise, while silently "fixing" a source libCZI could not open either
60
+ is a surprise rather than a service. Its output is then exactly as openable
61
+ as its input.
62
+ - ``tests/test_czi_foreign_readers.py`` never uses opencodecs to read. It
63
+ writes with opencodecs and opens with pylibCZIrw (libCZI itself, so what
64
+ ZEN and czicompress see), czifile (an independent implementation) and
65
+ aicspylibczi, skipping any reader that is absent rather than passing
66
+ silently. It covers all three compressions, a foreign decoder reading
67
+ pixels back exactly, both metadata regressions, a recompressed real file,
68
+ and 481 JPEG XR sub-blocks carried across. Its header records what to
69
+ assume when one fails: the writer is wrong, not the reader, because all
70
+ three open ZEN files and we are the new participant in a format we did not
71
+ define.
72
+ - ``tests/_czi_fixture.py`` takes the same default. That serializer is an
73
+ independent reimplementation of the layout, and
74
+ ``test_file_bytes_match_the_fixture_serializer`` compares the writer's bytes
75
+ against it, so the two have to agree on the default; it is written as a
76
+ literal on both sides rather than imported, because importing would make
77
+ them agree by construction and hollow the test out. Fixture files are also
78
+ the sources for the ``czi_recompress`` tests, so that corpus now produces
79
+ files libCZI accepts.
80
+ - Documented why the ZSTDHDR zstd level is 3 and not a parameter. Measured end
81
+ to end on a 503 MB uncompressed CZI, against ZEISS czicompress at 0.87 s and
82
+ 319.9 MB on the same file: level 3 gives 0.24 s and 318.3 MB, level 6 gives
83
+ 0.92 s and 315.3 MB, level 9 gives 1.38 s and 314.4 MB. The encode cost
84
+ transfers almost 1:1 into wall clock because this path is encode-bound
85
+ rather than I/O-bound, so 32 workers hide none of it, and level 6 is already
86
+ slower than the tool it replaces. About 1% of size for 4 to 6x the time is
87
+ not a trade worth a default. The hi-lo byte-plane shuffle is where the gain
88
+ actually is, and it is already on: on real microscopy pixels it takes the
89
+ ratio from 0.537 to 0.485 while being faster than raw zstd, 923 against
90
+ 199 MB/s.
91
+
13
92
  0.7.0 (2026-10-03)
14
93
  ------------------
15
94
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencodecs
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)
5
5
  Author: Kevin Cutler
6
6
  License-Expression: BSD-3-Clause
@@ -39,6 +39,18 @@ Requires-Dist: nd2; extra == "reference"
39
39
  Requires-Dist: readlif; extra == "reference"
40
40
  Requires-Dist: oiffile; extra == "reference"
41
41
  Requires-Dist: blosc2; extra == "reference"
42
+ Requires-Dist: zstandard; extra == "reference"
43
+ Requires-Dist: lz4; extra == "reference"
44
+ Requires-Dist: brotli; extra == "reference"
45
+ Requires-Dist: cramjam; extra == "reference"
46
+ Requires-Dist: lerc; extra == "reference"
47
+ Requires-Dist: zfpy; extra == "reference"
48
+ Requires-Dist: pcodec; extra == "reference"
49
+ Requires-Dist: ocf-blosc2; extra == "reference"
50
+ Requires-Dist: qoi; extra == "reference"
51
+ Requires-Dist: pillow-heif; extra == "reference"
52
+ Requires-Dist: pylibCZIrw; extra == "reference"
53
+ Requires-Dist: aicspylibczi; extra == "reference"
42
54
  Provides-Extra: test
43
55
  Requires-Dist: pytest>=8; extra == "test"
44
56
  Requires-Dist: pytest-cov; extra == "test"
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "opencodecs"
10
- version = "0.7.0"
10
+ version = "0.7.1"
11
11
  description = "Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -54,7 +54,21 @@ reference = [
54
54
  "nd2", # ND2
55
55
  "readlif", # LIF
56
56
  "oiffile", # OIB
57
- "blosc2", # MRCZ
57
+ "blosc2", # MRCZ, and Blosc2 / B2ND streams
58
+ # The libraries' own bindings, which test_foreign_readers.py decodes
59
+ # our streams with alongside imagecodecs.
60
+ "zstandard", # zstd
61
+ "lz4", # LZ4 frame
62
+ "brotli", # Brotli
63
+ "cramjam", # Snappy raw and framed
64
+ "lerc", # LERC
65
+ "zfpy", # ZFP
66
+ "pcodec", # pcodec
67
+ "ocf-blosc2", # Blosc2 in zarr (no zarr spec defines it)
68
+ "qoi", # QOI
69
+ "pillow-heif", # HEIF, libheif's decoder
70
+ "pylibCZIrw", # CZI through libCZI
71
+ "aicspylibczi", # CZI, the Allen Institute's libCZI binding
58
72
  ]
59
73
  test = [
60
74
  "pytest>=8",
@@ -349,4 +349,4 @@ __all__ = [
349
349
  "czi_recompress", "subblock_dims",
350
350
  ]
351
351
 
352
- __version__ = "0.7.0"
352
+ __version__ = "0.7.1"
@@ -813,6 +813,10 @@ class CziReader(Reader):
813
813
  finally:
814
814
  os.close(self._fd)
815
815
  self._owns_fd = False
816
+ # As on the positional-read path: a closed reader has no
817
+ # buffer, so the next call raises the reader's own "closed"
818
+ # error, not the mapping's "mmap closed or invalid".
819
+ self._mmap = None
816
820
 
817
821
  @classmethod
818
822
  def from_http(
@@ -70,6 +70,39 @@ _CMP_NAME_TO_CODE = {
70
70
  }
71
71
 
72
72
 
73
+ #: libCZI refuses metadata that is not rooted at ``ImageDocument``, with
74
+ #: ``Root-node "ImageDocument" not found``. That means czicompress and ZEN
75
+ #: cannot open the file at all -- not a degraded read, no read. The writer's
76
+ #: old default was a bare ``<Metadata/>``, which opencodecs' own reader
77
+ #: accepts, so every round-trip test passed while the files were unreadable
78
+ #: by every other CZI tool. Found 2026-10-04 when czicompress rejected a file
79
+ #: this writer had just produced.
80
+ #:
81
+ #: So metadata lacking the root is WRAPPED in one, and a caller writing a NEW
82
+ #: file cannot produce an unopenable one by accident or on purpose.
83
+ #:
84
+ #: ``metadata_as_is=True`` opts out, and exactly one caller needs it:
85
+ #: ``czi_recompress``, which promises the source's metadata is carried across
86
+ #: UNCHANGED. Rewriting it there would break that promise, and silently
87
+ #: "fixing" a source that libCZI could not open either is a surprise, not a
88
+ #: service -- the output is then exactly as openable as the input, which is
89
+ #: what faithful means.
90
+ _DEFAULT_METADATA = b"<ImageDocument><Metadata/></ImageDocument>"
91
+
92
+
93
+ def _ensure_image_document(xml: bytes) -> bytes:
94
+ """Return ``xml`` rooted at ImageDocument, wrapping it if it is not."""
95
+ head = xml.lstrip()
96
+ decl = b""
97
+ if head.startswith(b"<?xml"):
98
+ end = head.find(b"?>")
99
+ if end != -1:
100
+ decl, head = head[:end + 2], head[end + 2:].lstrip()
101
+ if head.startswith(b"<ImageDocument>") or head.startswith(b"<ImageDocument "):
102
+ return xml
103
+ return decl + b"<ImageDocument>" + head + b"</ImageDocument>"
104
+
105
+
73
106
  class CziWriterError(RuntimeError):
74
107
  """Raised on writer state-machine violations."""
75
108
 
@@ -263,6 +296,27 @@ def _zstdhdr_encode(pixel_bytes, itemsize: int, hilo: bool, *, scratch=None):
263
296
  before this returns, and verification later reads the encoded payload
264
297
  and the caller's own pixel snapshot rather than this buffer, so the
265
298
  next frame may reuse it.
299
+
300
+ WHY LEVEL 3, AND WHY IT IS NOT A PARAMETER
301
+ Measured end to end through this pipeline on a 503 MB uncompressed CZI
302
+ (2026-10-04), against ZEISS czicompress at 0.87 s / 319.9 MB on the same
303
+ file:
304
+
305
+ level 3 0.24 s 318.3 MB beats czicompress on size AND speed
306
+ level 6 0.92 s 315.3 MB 0.93% smaller, 3.91x slower
307
+ level 9 1.38 s 314.4 MB 1.23% smaller, 5.83x slower
308
+
309
+ The encode cost transfers almost 1:1 into wall clock, because this path is
310
+ encode-bound rather than I/O-bound: there is no I/O to hide it behind, even
311
+ with 32 workers. So a higher level buys about 1% of size for 4-6x the time,
312
+ and level 6 is already slower than the tool we replaced. Level 3 is the
313
+ only setting that wins on both axes.
314
+
315
+ The hi-lo shuffle, by contrast, is free and large: on real microscopy
316
+ pixels it took the ratio from 0.537 to 0.485 (9.7% smaller) while being
317
+ FASTER than raw zstd, 923 MB/s against 199 MB/s, because shuffled byte
318
+ planes are far more compressible. That is where the gain is, and it is
319
+ already on by default.
266
320
  """
267
321
  from .codecs._zstd import encode as zstd_encode
268
322
  if hilo and itemsize > 1:
@@ -519,15 +573,22 @@ class _CziStreamWriter(Writer):
519
573
  """Shared sub-block sink with optional bounded pixel verification."""
520
574
 
521
575
  def __init__(self, path: str | Path, *, compression: str = "none",
522
- hilo: bool = True, metadata_xml: bytes | str = b"<Metadata/>",
523
- verify: bool = False, background_encode: bool = False):
576
+ hilo: bool = True, metadata_xml: bytes | str | None = None,
577
+ verify: bool = False, background_encode: bool = False,
578
+ metadata_as_is: bool = False):
524
579
  if compression not in _CMP_NAME_TO_CODE:
525
580
  raise CziWriterError(f"unknown compression {compression!r}")
526
581
  self._path = Path(path)
527
582
  self._cmp_code = _CMP_NAME_TO_CODE[compression]
528
583
  self._hilo = bool(hilo)
529
- xml = metadata_xml.encode("utf-8") if isinstance(metadata_xml, str) else metadata_xml
530
- self._metadata_segment = _build_metadata_segment(xml)
584
+ if metadata_xml is None:
585
+ xml = _DEFAULT_METADATA
586
+ elif isinstance(metadata_xml, str):
587
+ xml = metadata_xml.encode("utf-8")
588
+ else:
589
+ xml = metadata_xml
590
+ self._metadata_segment = _build_metadata_segment(
591
+ xml if metadata_as_is else _ensure_image_document(xml))
531
592
  self._header_size = (32 + 88 + 31) // 32 * 32
532
593
  self._position = self._header_size + len(self._metadata_segment)
533
594
  self._entries: list[dict] = []
@@ -1119,7 +1180,7 @@ def czi_recompress(src, dst, *, compression: str = "zstdhdr", hilo: bool = True,
1119
1180
  n = len(reader.entries)
1120
1181
  if not n:
1121
1182
  raise CziWriterError(f"czi: {src} has no sub-blocks to recompress")
1122
- metadata = reader.metadata_bytes or b"<Metadata/>"
1183
+ metadata = reader.metadata_bytes or _DEFAULT_METADATA
1123
1184
 
1124
1185
  counts = {"recompressed": 0, "carried": 0}
1125
1186
 
@@ -1160,7 +1221,8 @@ def czi_recompress(src, dst, *, compression: str = "zstdhdr", hilo: bool = True,
1160
1221
  try:
1161
1222
  with CziWriter(str(dst), compression=compression, hilo=hilo,
1162
1223
  metadata_xml=metadata, verify=verify,
1163
- background_encode=background_encode) as writer:
1224
+ background_encode=background_encode,
1225
+ metadata_as_is=True) as writer:
1164
1226
  writer.write_many(frames(), workers=workers, copy_frames=False)
1165
1227
  except BaseException:
1166
1228
  # An interrupted rewrite must not leave a file behind. A CZI
@@ -139,6 +139,22 @@ class JpegXLReader(Reader):
139
139
  self._inner.close()
140
140
 
141
141
 
142
+ def jxl_level_as_quality(opts: dict) -> dict:
143
+ """imagecodecs.jpegxl_encode's ``level`` is a quality: 100 or below is
144
+ lossy at that quality, above 100 lossless. The encoder's ``quality``
145
+ has exactly that meaning, so ``level`` is passed as ``quality``.
146
+ Writers that take one compression level for every codec (TIFF,
147
+ NDTiff) reach JPEG XL through this too."""
148
+ if opts.get("level") is None:
149
+ opts.pop("level", None)
150
+ return opts
151
+ if opts.get("quality") is not None:
152
+ raise TypeError("jxl: pass level or quality, not both")
153
+ opts = dict(opts)
154
+ opts["quality"] = opts.pop("level")
155
+ return opts
156
+
157
+
142
158
  class JpegXLCodec(Codec):
143
159
  """Native JPEG XL codec (libjxl 0.11).
144
160
 
@@ -176,7 +192,7 @@ class JpegXLCodec(Codec):
176
192
  return _jxl_check_signature(head)
177
193
 
178
194
  def encode(self, arr: np.ndarray, *, dest=None, **opts) -> bytes | None:
179
- return _jxl_encode(arr, dest=dest, **opts)
195
+ return _jxl_encode(arr, dest=dest, **jxl_level_as_quality(opts))
180
196
 
181
197
  def decode(self, src: Any, **opts) -> np.ndarray:
182
198
  """Decode a JXL codestream.
@@ -496,7 +496,10 @@ def _run_decoder(name: str, cfg: dict, data: bytes) -> bytes:
496
496
  _DECODER_CACHE["blosc2"] = fn
497
497
  if fn:
498
498
  return fn(data)
499
- if name == "blosc2":
499
+ if name in ("blosc2", "numcodecs.blosc2"):
500
+ # "numcodecs.blosc2" is ocf-blosc2's v3 name, which our writer
501
+ # uses; plain "blosc2" is the v2 id, and what v3 stores this
502
+ # package wrote before 0.7.0 carry.
500
503
  fn = _DECODER_CACHE.get("blosc2")
501
504
  if not fn:
502
505
  from .codecs._blosc2 import decode as _b_decode
@@ -53,7 +53,10 @@ _CODEC_NAME_MAP = {
53
53
  "none": (None, None),
54
54
  "raw": (None, None),
55
55
  "zstd": ("zstd", "zstd"),
56
- "blosc2": ("blosc2", "blosc2"), # blosc2 wire format (v2 + v3)
56
+ # No zarr specification defines Blosc2. These are the names ocf-blosc2,
57
+ # the one numcodecs plugin for it, registers: "blosc2" for v2, and
58
+ # "numcodecs.blosc2" for v3, which is how zarr names a numcodecs codec.
59
+ "blosc2": ("blosc2", "numcodecs.blosc2"),
57
60
  "gzip": ("gzip", "gzip"),
58
61
  "blosc": ("blosc", None), # v2-only (no v3 spec)
59
62
  }
@@ -68,6 +71,16 @@ class OmeZarrWriterError(RuntimeError):
68
71
  # ---------------------------------------------------------------------------
69
72
 
70
73
 
74
+ def _blosc2_config(level: int | None, v3: bool = False) -> dict:
75
+ """The parameters ocf-blosc2's Blosc2(cname, clevel) takes, so zarr with
76
+ that plugin installed opens the store. cname names the inner codec;
77
+ _encode_chunk compresses with zstd, blosc2's default, and ocf-blosc2
78
+ reads "zstd" as that. (Before 0.7.0 the writer recorded "level", which
79
+ no reader but opencodecs accepted; those stores still read here.)"""
80
+ config = {"cname": "zstd", "clevel": level if level is not None else 5}
81
+ return config if v3 else {"id": "blosc2", **config}
82
+
83
+
71
84
  def _encode_chunk(raw: bytes, codec: str, level: int | None) -> bytes:
72
85
  """Compress one chunk's bytes."""
73
86
  if codec in (None, "none", "raw"):
@@ -309,8 +322,7 @@ def _write_v2(
309
322
  metadata["compressor"] = {"id": "zstd",
310
323
  "level": level if level is not None else 3}
311
324
  elif compressor == "blosc2":
312
- metadata["compressor"] = {"id": "blosc2",
313
- "level": level if level is not None else 5}
325
+ metadata["compressor"] = _blosc2_config(level)
314
326
  elif compressor == "gzip":
315
327
  metadata["compressor"] = {"id": "gzip",
316
328
  "level": level if level is not None else 6}
@@ -375,8 +387,8 @@ def _write_v3(
375
387
  })
376
388
  elif compressor == "blosc2":
377
389
  codecs.append({
378
- "name": "blosc2",
379
- "configuration": {"clevel": level if level is not None else 5},
390
+ "name": "numcodecs.blosc2",
391
+ "configuration": _blosc2_config(level, v3=True),
380
392
  })
381
393
  elif compressor == "gzip":
382
394
  codecs.append({
@@ -460,8 +472,8 @@ def _v3_codecs_for_shard_inner(
460
472
  })
461
473
  elif compressor == "blosc2":
462
474
  codecs.append({
463
- "name": "blosc2",
464
- "configuration": {"clevel": level if level is not None else 5},
475
+ "name": "numcodecs.blosc2",
476
+ "configuration": _blosc2_config(level, v3=True),
465
477
  })
466
478
  elif compressor == "gzip":
467
479
  codecs.append({
@@ -254,6 +254,9 @@ def encode_segment(data, codec: str | int, *, level: int | None = None,
254
254
  kw["level"] = level
255
255
  if code == JPEG2000:
256
256
  kw.setdefault("planar", False)
257
+ if code == JXL:
258
+ from .._jxl_codec import jxl_level_as_quality
259
+ kw = jxl_level_as_quality(kw)
257
260
  if code in (ZSTD, JPEG2000, JXL):
258
261
  from .pipeline import native_workers, in_worker
259
262
  # Zstandard counts background workers: zero means inline serial.
@@ -292,6 +295,9 @@ def bind_segment_encoder(codec: str | int, *, level=None, owned_output=False,
292
295
  options.setdefault("level", level)
293
296
  if code == JPEG2000:
294
297
  options.setdefault("planar", False)
298
+ if code == JXL:
299
+ from .._jxl_codec import jxl_level_as_quality
300
+ options = jxl_level_as_quality(options)
295
301
  eager = partial(fn, **options)
296
302
  if code not in (ZSTD, JPEG2000, JXL):
297
303
  return eager
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencodecs
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)
5
5
  Author: Kevin Cutler
6
6
  License-Expression: BSD-3-Clause
@@ -39,6 +39,18 @@ Requires-Dist: nd2; extra == "reference"
39
39
  Requires-Dist: readlif; extra == "reference"
40
40
  Requires-Dist: oiffile; extra == "reference"
41
41
  Requires-Dist: blosc2; extra == "reference"
42
+ Requires-Dist: zstandard; extra == "reference"
43
+ Requires-Dist: lz4; extra == "reference"
44
+ Requires-Dist: brotli; extra == "reference"
45
+ Requires-Dist: cramjam; extra == "reference"
46
+ Requires-Dist: lerc; extra == "reference"
47
+ Requires-Dist: zfpy; extra == "reference"
48
+ Requires-Dist: pcodec; extra == "reference"
49
+ Requires-Dist: ocf-blosc2; extra == "reference"
50
+ Requires-Dist: qoi; extra == "reference"
51
+ Requires-Dist: pillow-heif; extra == "reference"
52
+ Requires-Dist: pylibCZIrw; extra == "reference"
53
+ Requires-Dist: aicspylibczi; extra == "reference"
42
54
  Provides-Extra: test
43
55
  Requires-Dist: pytest>=8; extra == "test"
44
56
  Requires-Dist: pytest-cov; extra == "test"
@@ -306,6 +306,7 @@ tests/test_coverage_push.py
306
306
  tests/test_czi_bounded_iteration.py
307
307
  tests/test_czi_direct_decode.py
308
308
  tests/test_czi_fixture.py
309
+ tests/test_czi_foreign_readers.py
309
310
  tests/test_czi_mixed_subblocks.py
310
311
  tests/test_czi_parallel_write.py
311
312
  tests/test_czi_pread_indices.py
@@ -347,6 +348,7 @@ tests/test_filter_compat.py
347
348
  tests/test_filter_scratch.py
348
349
  tests/test_fits.py
349
350
  tests/test_fits_parallel.py
351
+ tests/test_foreign_readers.py
350
352
  tests/test_gif.py
351
353
  tests/test_gif_compositing.py
352
354
  tests/test_gzip_presized.py
@@ -17,6 +17,18 @@ nd2
17
17
  readlif
18
18
  oiffile
19
19
  blosc2
20
+ zstandard
21
+ lz4
22
+ brotli
23
+ cramjam
24
+ lerc
25
+ zfpy
26
+ pcodec
27
+ ocf-blosc2
28
+ qoi
29
+ pillow-heif
30
+ pylibCZIrw
31
+ aicspylibczi
20
32
 
21
33
  [reference:python_version >= "3.11"]
22
34
  czifile>=2026.3.12
@@ -0,0 +1,163 @@
1
+ """Every CZI opencodecs writes must open in the OTHER readers, not just ours.
2
+
3
+ WHY THIS FILE EXISTS
4
+ --------------------
5
+ On 2026-10-04 czicompress refused a file this writer had just produced:
6
+
7
+ An unrecoverable runtime error occurred: Root-node "ImageDocument" not found.
8
+
9
+ The writer's default metadata was a bare ``<Metadata/>``. opencodecs' own
10
+ reader accepts that, so every round-trip test in the suite passed while the
11
+ files were unopenable by libCZI, czicompress and ZEN -- which is to say, by
12
+ everything a user would actually hand the file to. The bug was not in any
13
+ code path; it was in the shape of the testing. A suite that only reads its
14
+ own output cannot find it.
15
+
16
+ So these tests never use opencodecs to read. They write with opencodecs and
17
+ open with:
18
+
19
+ pylibCZIrw the libCZI binding, i.e. what ZEN and czicompress use
20
+ czifile Christoph Gohlke's independent pure-Python reader
21
+ aicspylibczi the Allen Institute's libCZI binding
22
+
23
+ Any reader that is not installed is skipped rather than silently passed over,
24
+ so a thinned environment shows up as skips instead of false confidence.
25
+
26
+ WHAT TO DO WHEN ONE OF THESE FAILS
27
+ ----------------------------------
28
+ Assume the writer is wrong, not the reader. All three of these open files from
29
+ ZEN; we are the new participant in a format we did not define. A disagreement
30
+ between our reader and theirs means our reader is probably lenient in the same
31
+ way the writer is wrong, which is exactly how the metadata bug survived.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ import pathlib
36
+ import sys
37
+
38
+ import numpy as np
39
+ import pytest
40
+
41
+ sys.path.insert(0, str(pathlib.Path(__file__).parent))
42
+
43
+ from opencodecs import CziWriter, czi_recompress # noqa: E402
44
+
45
+ CORPUS = pathlib.Path(__file__).resolve().parents[1] / ".test_data" / "czi"
46
+
47
+
48
+ def _write(path, *, n=3, side=64, compression="none", **kw):
49
+ """A small multi-channel CZI, written by opencodecs."""
50
+ rs = np.random.RandomState(5)
51
+ with CziWriter(str(path), compression=compression, **kw) as w:
52
+ for c in range(n):
53
+ w.write_frame(rs.randint(0, 4096, (side, side)).astype("uint16"),
54
+ dims=[("X", 0, side, side), ("Y", 0, side, side),
55
+ ("C", c, 1, 1)])
56
+ return path
57
+
58
+
59
+ # --------------------------------------------------------------- pylibCZIrw
60
+ # The one that matters most: it is libCZI, so it is what ZEN and czicompress
61
+ # see. A file this reader rejects is a file our users cannot open.
62
+
63
+ @pytest.mark.parametrize("compression", ["none", "zstd", "zstdhdr"])
64
+ def test_pylibczirw_opens_what_we_write(tmp_path, compression):
65
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
66
+ src = _write(tmp_path / f"{compression}.czi", compression=compression)
67
+ with czirw.open_czi(str(src)) as r:
68
+ plane = r.read(plane={"C": 0})
69
+ assert plane is not None and plane.size > 0
70
+ assert r.total_bounding_rectangle[2:] == (64, 64)
71
+
72
+
73
+ def test_pylibczirw_reads_our_pixels_back_exactly(tmp_path):
74
+ """Lossless must mean lossless to a FOREIGN decoder, not only to ours."""
75
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
76
+ rs = np.random.RandomState(9)
77
+ frame = rs.randint(0, 4096, (64, 64)).astype("uint16")
78
+ out = tmp_path / "exact.czi"
79
+ with CziWriter(str(out), compression="zstdhdr") as w:
80
+ w.write_frame(frame, dims=[("X", 0, 64, 64), ("Y", 0, 64, 64),
81
+ ("C", 0, 1, 1)])
82
+ with czirw.open_czi(str(out)) as r:
83
+ got = np.squeeze(r.read(plane={"C": 0}))
84
+ assert np.array_equal(got, frame), "a foreign reader sees different pixels"
85
+
86
+
87
+ def test_default_metadata_is_openable_by_libczi(tmp_path):
88
+ """The exact bug this file was created for.
89
+
90
+ The old default produced files libCZI refused outright. Asserting through
91
+ our own reader could never have caught it.
92
+ """
93
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
94
+ src = _write(tmp_path / "default_meta.czi") # no metadata_xml passed
95
+ with czirw.open_czi(str(src)) as r:
96
+ assert r.total_bounding_rectangle is not None
97
+
98
+
99
+ def test_rootless_metadata_still_opens_because_it_is_wrapped(tmp_path):
100
+ """Handing in the old broken value must not produce an unopenable file."""
101
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
102
+ src = _write(tmp_path / "rootless.czi", metadata_xml=b"<Metadata/>")
103
+ with czirw.open_czi(str(src)) as r:
104
+ assert r.total_bounding_rectangle is not None
105
+
106
+
107
+ # ------------------------------------------------------------------ czifile
108
+ # An independent implementation, not libCZI. It disagrees with libCZI in
109
+ # places, which is the point of having both.
110
+
111
+ def test_czifile_opens_what_we_write(tmp_path):
112
+ czifile = pytest.importorskip("czifile", reason="czifile missing")
113
+ src = _write(tmp_path / "cf.czi", compression="zstdhdr")
114
+ with czifile.CziFile(str(src)) as c:
115
+ # czifile exposes the directory and asarray(), not a .shape attribute.
116
+ assert len(c.filtered_subblock_directory) == 3, "sub-blocks not seen"
117
+ data = c.asarray()
118
+ assert data.size > 0 and data.dtype == np.uint16
119
+
120
+
121
+ # ------------------------------------------------------------- aicspylibczi
122
+
123
+ def test_aicspylibczi_opens_what_we_write(tmp_path):
124
+ aics = pytest.importorskip("aicspylibczi", reason="aicspylibczi missing")
125
+ src = _write(tmp_path / "aics.czi", compression="zstdhdr")
126
+ czi = aics.CziFile(str(src))
127
+ assert czi.get_dims_shape() is not None
128
+ img, _shape = czi.read_image(C=0)
129
+ assert img.size > 0
130
+
131
+
132
+ # ------------------------------------------- recompressed real files
133
+ # czi_recompress output is what the ingest pipeline actually produces, so it
134
+ # is the output that most needs to open elsewhere.
135
+
136
+ @pytest.mark.skipif(not (CORPUS / "idr0011_plate1_scene1.czi").is_file(),
137
+ reason="corpus CZI not present")
138
+ def test_recompressed_real_file_opens_in_libczi(tmp_path):
139
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
140
+ dst = tmp_path / "re.czi"
141
+ czi_recompress(CORPUS / "idr0011_plate1_scene1.czi", dst,
142
+ compression="zstdhdr")
143
+ with czirw.open_czi(str(dst)) as r:
144
+ assert r.total_bounding_rectangle is not None
145
+
146
+
147
+ @pytest.mark.skipif(not (CORPUS / "ome_axioscan_pyramid.czi").is_file(),
148
+ reason="corpus slide scan not present")
149
+ def test_carried_jpegxr_file_opens_in_libczi(tmp_path):
150
+ """Carrying lossy sub-blocks across must leave a file libCZI still reads.
151
+
152
+ This is the strongest interop case available: 481 JPEG XR sub-blocks
153
+ copied verbatim into a container our writer built. If the container is
154
+ wrong, libCZI will say so even though the payloads are untouched.
155
+ """
156
+ czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
157
+ dst = tmp_path / "carried.czi"
158
+ info = czi_recompress(CORPUS / "ome_axioscan_pyramid.czi", dst,
159
+ compression="zstdhdr",
160
+ recompress_when=lambda e: e.compression == 0)
161
+ assert info["carried"] == info["subblocks"]
162
+ with czirw.open_czi(str(dst)) as r:
163
+ assert r.total_bounding_rectangle is not None
@@ -73,7 +73,9 @@ def test_payload_buffers_are_reused(path):
73
73
  def test_payload_location_is_cached(path):
74
74
  with _open(path) as r:
75
75
  r.read()
76
- assert len(r._payload_ranges) == (len(r) if _czi_reader._PAYLOAD_PREAD else 0)
76
+ # Every source type computes and keeps its payload ranges, the
77
+ # mapping (no positional reads, e.g. Windows) as well.
78
+ assert len(r._payload_ranges) == len(r)
77
79
 
78
80
 
79
81
  def test_concurrent_tile_reads_on_one_reader(path, frames):
@@ -231,4 +231,10 @@ def test_stored_stack_reads_on_more_than_one_worker(monkeypatch, tmp_path, n):
231
231
  assert np.array_equal(out, np.stack(tiles, axis=0))
232
232
  n_tasks, workers = seen[-1]
233
233
  assert n_tasks == n
234
- assert workers >= 2
234
+ if cz._PAYLOAD_PREAD:
235
+ # Positional reads overlap their I/O across workers.
236
+ assert workers >= 2
237
+ else:
238
+ # Without positional reads (Windows) a stored stack is a copy out
239
+ # of the mapping, which did not scale across workers when measured.
240
+ assert workers == 1