Skip to content

Commit

Permalink
avif: increment references rather than memcpy bytes arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Jan 20, 2021
1 parent 38f4d48 commit f28e375
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 39 deletions.
9 changes: 6 additions & 3 deletions src/PIL/AvifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ def _save_all(im, fp, filename):
exif = exif.tobytes()
xmp = info.get("xmp", im.info.get("xmp") or im.info.get("XML:com.adobe.xmp"))

if isinstance(xmp, str):
xmp = xmp.encode("utf-8")

# Setup the AVIF encoder
enc = _avif.AvifEncoder(
im.size[0],
Expand All @@ -122,9 +125,9 @@ def _save_all(im, fp, filename):
speed,
codec,
range_,
icc_profile,
exif,
xmp,
icc_profile or b"",
exif or b"",
xmp or b"",
)

# Add each frame
Expand Down
82 changes: 46 additions & 36 deletions src/_avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ typedef struct {
PyObject_HEAD
avifEncoder *encoder;
avifImage *image;
PyObject *icc_bytes;
PyObject *exif_bytes;
PyObject *xmp_bytes;
int frame_index;
} AvifEncoderObject;

Expand All @@ -36,8 +39,7 @@ static PyTypeObject AvifEncoder_Type;
typedef struct {
PyObject_HEAD
avifDecoder *decoder;
uint8_t *data;
Py_ssize_t size;
PyObject *data;
char *mode;
} AvifDecoderObject;

Expand Down Expand Up @@ -113,19 +115,16 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
int qmin_alpha = AVIF_QUANTIZER_LOSSLESS; // =0
int qmax_alpha = AVIF_QUANTIZER_LOSSLESS; // =0
int speed = 8;
uint8_t *icc_bytes;
uint8_t *exif_bytes;
uint8_t *xmp_bytes;
Py_ssize_t icc_size;
Py_ssize_t exif_size;
Py_ssize_t xmp_size;
PyObject *icc_bytes;
PyObject *exif_bytes;
PyObject *xmp_bytes;

char *codec = "auto";
char *range = "full";

if (!PyArg_ParseTuple(
args,
"iisiiiiissz#z#z#",
"iisiiiiissSSS",
&width,
&height,
&yuv_format,
Expand All @@ -137,11 +136,8 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
&codec,
&range,
&icc_bytes,
&icc_size,
&exif_bytes,
&exif_size,
&xmp_bytes,
&xmp_size)) {
&xmp_bytes)) {
return NULL;
}

Expand Down Expand Up @@ -205,6 +201,10 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
// Create a new animation encoder and picture frame
self = PyObject_New(AvifEncoderObject, &AvifEncoder_Type);
if (self) {
self->icc_bytes = NULL;
self->exif_bytes = NULL;
self->xmp_bytes = NULL;

encoder = avifEncoderCreate();
encoder->maxThreads = max_threads;
encoder->minQuantizer = enc_options.qmin;
Expand All @@ -227,18 +227,33 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
image->height = height;
image->depth = 8;

if (icc_size) {
avifImageSetProfileICC(image, icc_bytes, icc_size);
if (PyBytes_GET_SIZE(icc_bytes)) {
self->icc_bytes = icc_bytes;
Py_INCREF(icc_bytes);
avifImageSetProfileICC(
image,
(uint8_t *)PyBytes_AS_STRING(icc_bytes),
PyBytes_GET_SIZE(icc_bytes));
} else {
image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
}

if (exif_size) {
avifImageSetMetadataExif(image, exif_bytes, exif_size);
if (PyBytes_GET_SIZE(exif_bytes)) {
self->exif_bytes = exif_bytes;
Py_INCREF(exif_bytes);
avifImageSetMetadataExif(
image,
(uint8_t *)PyBytes_AS_STRING(exif_bytes),
PyBytes_GET_SIZE(exif_bytes));
}
if (xmp_size) {
avifImageSetMetadataXMP(image, xmp_bytes, xmp_size);
if (PyBytes_GET_SIZE(xmp_bytes)) {
self->xmp_bytes = xmp_bytes;
Py_INCREF(xmp_bytes);
avifImageSetMetadataXMP(
image,
(uint8_t *)PyBytes_AS_STRING(xmp_bytes),
PyBytes_GET_SIZE(xmp_bytes));
}

self->image = image;
Expand All @@ -258,6 +273,9 @@ _encoder_dealloc(AvifEncoderObject *self) {
if (self->image) {
avifImageDestroy(self->image);
}
Py_XDECREF(self->icc_bytes);
Py_XDECREF(self->exif_bytes);
Py_XDECREF(self->xmp_bytes);
Py_RETURN_NONE;
}

Expand Down Expand Up @@ -430,8 +448,7 @@ _encoder_finish(AvifEncoderObject *self) {
// Decoder functions
PyObject *
AvifDecoderNew(PyObject *self_, PyObject *args) {
const uint8_t *avif_bytes;
Py_ssize_t size;
PyObject *avif_bytes;
AvifDecoderObject *self = NULL;

char *upsampling_str;
Expand All @@ -441,8 +458,7 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {

avifResult result;

if (!PyArg_ParseTuple(
args, "z#ss", &avif_bytes, &size, &codec_str, &upsampling_str)) {
if (!PyArg_ParseTuple(args, "Sss", &avif_bytes, &codec_str, &upsampling_str)) {
return NULL;
}

Expand Down Expand Up @@ -484,26 +500,20 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
return NULL;
}
self->decoder = NULL;
self->size = size;

// We need to allocate storage for the decoder for the lifetime of the object
// (avifDecoderSetIOMemory does not copy the data passed into it)
self->data = PyMem_New(uint8_t, size);
if (self->data == NULL) {
PyErr_SetString(PyExc_MemoryError, "PyMem_New() failed");
Py_DECREF(self);
return NULL;
}

memcpy(self->data, avif_bytes, size);
Py_INCREF(avif_bytes);
self->data = avif_bytes;

self->decoder = avifDecoderCreate();
#if AVIF_VERSION >= 80400
self->decoder->maxThreads = max_threads;
#endif
self->decoder->codecChoice = codec;

avifDecoderSetIOMemory(self->decoder, self->data, self->size);
avifDecoderSetIOMemory(
self->decoder,
(uint8_t *)PyBytes_AS_STRING(self->data),
PyBytes_GET_SIZE(self->data));

result = avifDecoderParse(self->decoder);

Expand Down Expand Up @@ -532,7 +542,7 @@ _decoder_dealloc(AvifDecoderObject *self) {
if (self->decoder) {
avifDecoderDestroy(self->decoder);
}
PyMem_Free(self->data);
Py_XDECREF(self->data);
Py_RETURN_NONE;
}

Expand Down

0 comments on commit f28e375

Please sign in to comment.