Skip to content

Commit

Permalink
libheif 1.17.1 support (#156)
Browse files Browse the repository at this point in the history
* fixed "heif_writer callback returned a null error text"
* fix of writing NCLX color profile for libheif 1.17.0
* disabled two AVIF tests with aom=3.3.0

---------

Signed-off-by: Alexander Piskun <[email protected]>
  • Loading branch information
bigcat88 authored Oct 24, 2023
1 parent b1e77b6 commit c9f2e69
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 17 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/analysis-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ jobs:

coverage-linux:
runs-on: ubuntu-22.04
name: Coverage(Linux) • 🐍3.12
name: Coverage(Linux) • 🐍3.11

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.12'
python-version: '3.11'

- name: Prepare system
run: |
Expand Down Expand Up @@ -78,13 +78,13 @@ jobs:

coverage-linux-pillow-dev:
runs-on: ubuntu-22.04
name: Coverage(Linux, Pillow-dev) • 🐍3.11
name: Coverage(Linux, Pillow-dev) • 🐍3.12

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
python-version: '3.12'

- name: Prepare system
run: |
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
All notable changes to this project will be documented in this file.

## [0.14.0 - 2023-11-xx]

### Fixed

- Support of libheif `1.17.0`. #156

## [0.13.1 - 2023-10-15]

### Added
Expand Down
25 changes: 14 additions & 11 deletions pillow_heif/_pillow_heif.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#define RETURN_NONE Py_INCREF(Py_None); return Py_None;

static struct heif_error heif_error_no = { .code = 0, .subcode = 0, .message = NULL };
static struct heif_error heif_error_no = { .code = 0, .subcode = 0, .message = "" };

int check_error(struct heif_error error) {
if (error.code == heif_error_Ok) {
Expand Down Expand Up @@ -57,6 +57,7 @@ typedef struct {
enum heif_chroma chroma;
struct heif_image* image;
struct heif_image_handle* handle;
struct heif_color_profile_nclx* output_nclx_color_profile;
} CtxWriteImageObject;

static PyTypeObject CtxWriteImage_Type;
Expand Down Expand Up @@ -110,6 +111,8 @@ static void _CtxWriteImage_destructor(CtxWriteImageObject* self) {
heif_image_handle_release(self->handle);
if (self->image)
heif_image_release(self->image);
if (self->output_nclx_color_profile)
heif_nclx_color_profile_free(self->output_nclx_color_profile);
PyObject_Del(self);
}

Expand Down Expand Up @@ -496,22 +499,17 @@ static PyObject* _CtxWriteImage_set_icc_profile(CtxWriteImageObject* self, PyObj

static PyObject* _CtxWriteImage_set_nclx_profile(CtxWriteImageObject* self, PyObject* args) {
/* color_primaries: int, transfer_characteristics: int, matrix_coefficients: int, full_range_flag: int */
struct heif_error error;
int color_primaries, transfer_characteristics, matrix_coefficients, full_range_flag;

if (!PyArg_ParseTuple(args, "iiii",
&color_primaries, &transfer_characteristics, &matrix_coefficients, &full_range_flag))
return NULL;

struct heif_color_profile_nclx* nclx_color_profile = heif_nclx_color_profile_alloc();
nclx_color_profile->color_primaries = color_primaries;
nclx_color_profile->transfer_characteristics = transfer_characteristics;
nclx_color_profile->matrix_coefficients = matrix_coefficients;
nclx_color_profile->full_range_flag = full_range_flag;
error = heif_image_set_nclx_color_profile(self->image, nclx_color_profile);
heif_nclx_color_profile_free(nclx_color_profile);
if (check_error(error))
return NULL;
self->output_nclx_color_profile = heif_nclx_color_profile_alloc();
self->output_nclx_color_profile->color_primaries = color_primaries;
self->output_nclx_color_profile->transfer_characteristics = transfer_characteristics;
self->output_nclx_color_profile->matrix_coefficients = matrix_coefficients;
self->output_nclx_color_profile->full_range_flag = full_range_flag;
RETURN_NONE
}

Expand All @@ -528,6 +526,10 @@ static PyObject* _CtxWriteImage_encode(CtxWriteImageObject* self, PyObject* args
Py_BEGIN_ALLOW_THREADS
options = heif_encoding_options_alloc();
options->macOS_compatibility_workaround_no_nclx_profile = !save_nclx;
if (!self->output_nclx_color_profile && save_nclx)
self->output_nclx_color_profile = heif_nclx_color_profile_alloc();
if (self->output_nclx_color_profile)
options->output_nclx_profile = self->output_nclx_color_profile;
error = heif_context_encode_image(ctx_write->ctx, self->image, ctx_write->encoder, options, &self->handle);
heif_encoding_options_free(options);
Py_END_ALLOW_THREADS
Expand Down Expand Up @@ -679,6 +681,7 @@ static PyObject* _CtxWriteImage_create(CtxWriteObject* self, PyObject* args) {
ctx_write_image->chroma = chroma;
ctx_write_image->image = image;
ctx_write_image->handle = NULL;
ctx_write_image->output_nclx_color_profile = NULL;
return (PyObject*)ctx_write_image;
}

Expand Down
1 change: 1 addition & 0 deletions tests/basic_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_libheif_info():
"1.15.2",
"1.16.1",
"1.16.2",
"1.17.1",
)


Expand Down
8 changes: 8 additions & 0 deletions tests/metadata_etc_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def test_pillow_primary_image(save_format):

@pytest.mark.skipif(not aom(), reason="Requires AVIF support.")
@pytest.mark.skipif(not hevc_enc(), reason="Requires HEVC encoder.")
@pytest.mark.skipif(
pillow_heif.libheif_info().get("AVIF", "").find("v3.3.0") != -1,
reason="libheif 1.17.1 fails this test with this AOM version.",
)
@pytest.mark.parametrize("save_format", ("HEIF", "AVIF"))
def test_heif_info_changing(save_format):
xmp = b"LeagueOf"
Expand Down Expand Up @@ -111,6 +115,10 @@ def test_heif_info_changing(save_format):

@pytest.mark.skipif(not aom(), reason="Requires AVIF support.")
@pytest.mark.skipif(not hevc_enc(), reason="Requires HEVC encoder.")
@pytest.mark.skipif(
pillow_heif.libheif_info().get("AVIF", "").find("v3.3.0") != -1,
reason="libheif 1.17.1 fails this test with this AOM version.",
)
@pytest.mark.parametrize("save_format", ("HEIF", "AVIF"))
def test_pillow_info_changing(save_format):
xmp = b"LeagueOf"
Expand Down
7 changes: 5 additions & 2 deletions tests/write_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import helpers
import pytest
from packaging.version import parse as parse_version
from PIL import Image, ImageSequence

import pillow_heif
Expand Down Expand Up @@ -503,7 +504,9 @@ def test_nclx_profile_write():
}
im_rgb.save(buf, format="HEIF")
nclx_out = Image.open(buf).info["nclx_profile"]
for k in im_rgb.info["nclx_profile"]:
assert im_rgb.info["nclx_profile"][k] == nclx_out[k]
if parse_version(pillow_heif.libheif_version()) >= parse_version("1.17.0"):
# in libheif 1.17.0 logic of this was corrected: https://github.com/strukturag/libheif/issues/995
for k in im_rgb.info["nclx_profile"]:
assert im_rgb.info["nclx_profile"][k] == nclx_out[k]
finally:
pillow_heif.options.SAVE_NCLX_PROFILE = False

0 comments on commit c9f2e69

Please sign in to comment.