22#include <Python.h>
33#include "libImaging/Imaging.h"
44
5- #include <jxl/codestream_header.h>
65#include <jxl/decode.h>
7- #include <jxl/types.h>
86#include <jxl/thread_parallel_runner.h>
97
108#define _JXL_CHECK (call_name ) \
@@ -25,13 +23,9 @@ _jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) {
2523 pf -> data_type = JXL_TYPE_UINT8 ;
2624 }
2725
28- // this *might* cause some issues on Big-Endian systems
29- // would be great to test it
30- pf -> endianness = JXL_NATIVE_ENDIAN ;
3126 pf -> align = 0 ;
3227}
3328
34- // TODO: floating point mode
3529char *
3630_jxl_get_mode (const JxlBasicInfo * bi ) {
3731 if (bi -> num_color_channels == 1 && !bi -> alpha_bits ) {
@@ -41,19 +35,13 @@ _jxl_get_mode(const JxlBasicInfo *bi) {
4135 }
4236
4337 if (bi -> bits_per_sample == 8 ) {
44- // image has transparency
4538 if (bi -> alpha_bits ) {
39+ // image has transparency
4640 if (bi -> num_color_channels == 3 ) {
47- if (bi -> alpha_premultiplied ) {
48- return "RGBa" ;
49- }
50- return "RGBA" ;
41+ return bi -> alpha_premultiplied ? "RGBa" : "RGBA" ;
5142 }
5243 if (bi -> num_color_channels == 1 ) {
53- if (bi -> alpha_premultiplied ) {
54- return "La" ;
55- }
56- return "LA" ;
44+ return bi -> alpha_premultiplied ? "La" : "LA" ;
5745 }
5846 } else {
5947 // image has no transparency
@@ -78,8 +66,8 @@ typedef struct {
7866 uint8_t * jxl_data ; // input jxl bitstream
7967 Py_ssize_t jxl_data_len ; // length of input jxl bitstream
8068
81- uint8_t * outbuf ;
82- size_t outbuf_len ;
69+ uint8_t * output_buffer ;
70+ size_t output_buffer_len ;
8371
8472 uint8_t * jxl_icc ;
8573 size_t jxl_icc_len ;
@@ -106,10 +94,10 @@ _jxl_decoder_dealloc(PyObject *self) {
10694 decp -> jxl_data = NULL ;
10795 decp -> jxl_data_len = 0 ;
10896 }
109- if (decp -> outbuf ) {
110- free (decp -> outbuf );
111- decp -> outbuf = NULL ;
112- decp -> outbuf_len = 0 ;
97+ if (decp -> output_buffer ) {
98+ free (decp -> output_buffer );
99+ decp -> output_buffer = NULL ;
100+ decp -> output_buffer_len = 0 ;
113101 }
114102 if (decp -> jxl_icc ) {
115103 free (decp -> jxl_icc );
@@ -139,7 +127,6 @@ _jxl_decoder_dealloc(PyObject *self) {
139127}
140128
141129// sets input jxl bitstream loaded into jxl_data
142- // has to be called after every rewind
143130void
144131_jxl_decoder_set_input (PyObject * self ) {
145132 JpegXlDecoderObject * decp = (JpegXlDecoderObject * )self ;
@@ -184,28 +171,28 @@ PyObject *
184171_jxl_decoder_new (PyObject * self , PyObject * args ) {
185172 PyBytesObject * jxl_string ;
186173
174+ // parse one argument which is a string with jxl data
175+ if (!PyArg_ParseTuple (args , "O" , & jxl_string )) {
176+ return NULL ;
177+ }
178+
187179 JpegXlDecoderObject * decp = NULL ;
188180 decp = PyObject_New (JpegXlDecoderObject , & JpegXlDecoder_Type );
189- decp -> mode = NULL ;
190181 decp -> jxl_data = NULL ;
191182 decp -> jxl_data_len = 0 ;
192- decp -> outbuf = NULL ;
193- decp -> outbuf_len = 0 ;
183+ decp -> output_buffer = NULL ;
184+ decp -> output_buffer_len = 0 ;
194185 decp -> jxl_icc = NULL ;
195186 decp -> jxl_icc_len = 0 ;
196187 decp -> jxl_exif = NULL ;
197188 decp -> jxl_exif_len = 0 ;
198189 decp -> jxl_xmp = NULL ;
199190 decp -> jxl_xmp_len = 0 ;
191+ decp -> mode = NULL ;
200192
201193 // used for printing more detailed error messages
202194 char * jxl_call_name ;
203195
204- // parse one argument which is a string with jxl data
205- if (!PyArg_ParseTuple (args , "S" , & jxl_string )) {
206- return NULL ;
207- }
208-
209196 // this data needs to be copied to JpegXlDecoderObject
210197 // so that input bitstream is preserved across calls
211198 const uint8_t * _tmp_jxl_data ;
@@ -216,7 +203,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
216203 (PyObject * )jxl_string , (char * * )& _tmp_jxl_data , & _tmp_jxl_data_len
217204 );
218205
219- // here occurs this copying (inefficiency)
220206 decp -> jxl_data = malloc (_tmp_jxl_data_len );
221207 memcpy (decp -> jxl_data , _tmp_jxl_data , _tmp_jxl_data_len );
222208 decp -> jxl_data_len = _tmp_jxl_data_len ;
@@ -250,7 +236,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
250236
251237decoder_loop_skip_process :
252238
253- // there was an error at JxlDecoderProcessInput stage
254239 if (decp -> status == JXL_DEC_ERROR ) {
255240 jxl_call_name = "JxlDecoderProcessInput" ;
256241 goto end ;
@@ -262,11 +247,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
262247
263248 _jxl_get_pixel_format (& decp -> pixel_format , & decp -> basic_info );
264249 decp -> mode = _jxl_get_mode (& decp -> basic_info );
265-
266- continue ;
267- }
268-
269- if (decp -> status == JXL_DEC_COLOR_ENCODING ) {
250+ } else if (decp -> status == JXL_DEC_COLOR_ENCODING ) {
270251 decp -> status = JxlDecoderGetICCProfileSize (
271252 decp -> decoder , JXL_COLOR_PROFILE_TARGET_DATA , & decp -> jxl_icc_len
272253 );
@@ -285,49 +266,45 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
285266 decp -> jxl_icc_len
286267 );
287268 _JXL_CHECK ("JxlDecoderGetColorAsICCProfile" );
288-
289- continue ;
290- }
291-
292- if (decp -> status == JXL_DEC_BOX ) {
293- char btype [4 ];
294- decp -> status = JxlDecoderGetBoxType (decp -> decoder , btype , JXL_TRUE );
269+ } else if (decp -> status == JXL_DEC_BOX ) {
270+ char box_type [4 ];
271+ decp -> status = JxlDecoderGetBoxType (decp -> decoder , box_type , JXL_TRUE );
295272 _JXL_CHECK ("JxlDecoderGetBoxType" );
296273
297- int is_box_exif = !memcmp (btype , "Exif" , 4 );
298- int is_box_xmp = !memcmp (btype , "xml " , 4 );
274+ int is_box_exif = !memcmp (box_type , "Exif" , 4 );
275+ int is_box_xmp = is_box_exif ? 0 : !memcmp (box_type , "xml " , 4 );
299276 if (!is_box_exif && !is_box_xmp ) {
300277 // not exif/xmp box so continue
301278 continue ;
302279 }
303280
304- uint64_t cur_compr_box_size ;
305- decp -> status = JxlDecoderGetBoxSizeRaw (decp -> decoder , & cur_compr_box_size );
281+ uint64_t compressed_box_size ;
282+ decp -> status = JxlDecoderGetBoxSizeRaw (decp -> decoder , & compressed_box_size );
306283 _JXL_CHECK ("JxlDecoderGetBoxSizeRaw" );
307284
308285 uint8_t * final_jxl_buf = NULL ;
309286 Py_ssize_t final_jxl_buf_len = 0 ;
310287
311- // cur_box_size is actually compressed box size
312- // it will also serve as our chunk size
313288 do {
314289 uint8_t * _new_jxl_buf =
315- realloc (final_jxl_buf , final_jxl_buf_len + cur_compr_box_size );
290+ realloc (final_jxl_buf , final_jxl_buf_len + compressed_box_size );
316291 if (!_new_jxl_buf ) {
317292 PyErr_SetString (PyExc_OSError , "failed to allocate final_jxl_buf" );
318293 goto end ;
319294 }
320295 final_jxl_buf = _new_jxl_buf ;
321296
322297 decp -> status = JxlDecoderSetBoxBuffer (
323- decp -> decoder , final_jxl_buf + final_jxl_buf_len , cur_compr_box_size
298+ decp -> decoder ,
299+ final_jxl_buf + final_jxl_buf_len ,
300+ compressed_box_size
324301 );
325302 _JXL_CHECK ("JxlDecoderSetBoxBuffer" );
326303
327304 decp -> status = JxlDecoderProcessInput (decp -> decoder );
328305
329306 size_t remaining = JxlDecoderReleaseBoxBuffer (decp -> decoder );
330- final_jxl_buf_len += ( cur_compr_box_size - remaining ) ;
307+ final_jxl_buf_len += compressed_box_size - remaining ;
331308 } while (decp -> status == JXL_DEC_BOX_NEED_MORE_OUTPUT );
332309
333310 if (is_box_exif ) {
@@ -409,8 +386,8 @@ _jxl_decoder_get_next(PyObject *self) {
409386 while (decp -> status != JXL_DEC_NEED_IMAGE_OUT_BUFFER ) {
410387 decp -> status = JxlDecoderProcessInput (decp -> decoder );
411388
412- // this should only occur after rewind
413389 if (decp -> status == JXL_DEC_NEED_MORE_INPUT ) {
390+ // this should only occur after rewind
414391 _jxl_decoder_set_input ((PyObject * )decp );
415392 _JXL_CHECK ("JxlDecoderSetInput" )
416393 } else if (decp -> status == JXL_DEC_FRAME ) {
@@ -420,37 +397,40 @@ _jxl_decoder_get_next(PyObject *self) {
420397 }
421398 }
422399
423- size_t new_outbuf_len ;
400+ size_t new_output_buffer_len ;
424401 decp -> status = JxlDecoderImageOutBufferSize (
425- decp -> decoder , & decp -> pixel_format , & new_outbuf_len
402+ decp -> decoder , & decp -> pixel_format , & new_output_buffer_len
426403 );
427404 _JXL_CHECK ("JxlDecoderImageOutBufferSize" );
428405
429406 // only allocate memory when current buffer is too small
430- if (decp -> outbuf_len < new_outbuf_len ) {
431- decp -> outbuf_len = new_outbuf_len ;
432- uint8_t * _new_outbuf = realloc (decp -> outbuf , decp -> outbuf_len );
433- if (!_new_outbuf ) {
434- PyErr_SetString (PyExc_OSError , "failed to allocate outbuf" );
407+ if (decp -> output_buffer_len < new_output_buffer_len ) {
408+ decp -> output_buffer_len = new_output_buffer_len ;
409+ uint8_t * new_output_buffer =
410+ realloc (decp -> output_buffer , decp -> output_buffer_len );
411+ if (!new_output_buffer ) {
412+ PyErr_SetString (PyExc_OSError , "failed to allocate buffer" );
435413 return NULL ;
436414 }
437- decp -> outbuf = _new_outbuf ;
415+ decp -> output_buffer = new_output_buffer ;
438416 }
439417
440418 decp -> status = JxlDecoderSetImageOutBuffer (
441- decp -> decoder , & decp -> pixel_format , decp -> outbuf , decp -> outbuf_len
419+ decp -> decoder , & decp -> pixel_format , decp -> output_buffer , decp -> output_buffer_len
442420 );
443421 _JXL_CHECK ("JxlDecoderSetImageOutBuffer" );
444422
445- // decode image into output_buffer
423+ // decode image into output buffer
446424 decp -> status = JxlDecoderProcessInput (decp -> decoder );
447425
448426 if (decp -> status != JXL_DEC_FULL_IMAGE ) {
449427 PyErr_SetString (PyExc_OSError , "failed to read next frame" );
450428 return NULL ;
451429 }
452430
453- bytes = PyBytes_FromStringAndSize ((char * )(decp -> outbuf ), decp -> outbuf_len );
431+ bytes = PyBytes_FromStringAndSize (
432+ (char * )(decp -> output_buffer ), decp -> output_buffer_len
433+ );
454434
455435 ret = Py_BuildValue ("SIi" , bytes , fhdr .duration , fhdr .is_last );
456436
0 commit comments