--- CV/CV.xs 2017/12/27 17:48:16 1.56 +++ CV/CV.xs 2021/01/04 04:49:04 1.58 @@ -26,13 +26,16 @@ #include #endif +#if JXL +#include +#include "jxl/thread_parallel_runner.h" +#endif + #include "perlmulticore.h" #define IW 80 /* MUST match Schnauzer.pm! */ #define IH 60 /* MUST match Schnauzer.pm! */ -#define RAND (seed = (seed + 7141) * 54773 % 134456) - #define LINELENGTH 240 #define ELLIPSIS "\xe2\x80\xa6" @@ -215,20 +218,26 @@ static boolean cv_ms_fill (j_decompress_ptr cinfo) { - ERREXIT (cinfo, JERR_INPUT_EMPTY); + // unexpected EOF, warn and generate fake EOI marker + + WARNMS (cinfo, JWRN_JPEG_EOF); + + struct jpeg_source_mgr *src = (struct jpeg_source_mgr *)cinfo->src; + + static const JOCTET eoi[] = { 0xFF, JPEG_EOI }; + + src->next_input_byte = eoi; + src->bytes_in_buffer = sizeof (eoi); return TRUE; } static void cv_ms_skip (j_decompress_ptr cinfo, long num_bytes) { - if (num_bytes > 0) - { - struct jpeg_source_mgr *src = (struct jpeg_source_mgr *)cinfo->src; + struct jpeg_source_mgr *src = (struct jpeg_source_mgr *)cinfo->src; - src->next_input_byte += num_bytes; - src->bytes_in_buffer -= num_bytes; - } + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; } static void cv_jpeg_mem_src (j_decompress_ptr cinfo, void *buf, size_t buflen) @@ -253,6 +262,45 @@ ///////////////////////////////////////////////////////////////////////////// +/* great, the jpeg-xl reference implementaton requires us to parse bmff files */ + +struct bmff_box +{ + char type[4]; + const uint8_t *ptr; + size_t size; +}; + +static int +bmff_parse_box (struct bmff_box *box, const uint8_t **next_in, size_t *avail_in) +{ + if (*avail_in < 8) + return 0; + + box->size = ((*next_in)[0] << 24) + | ((*next_in)[1] << 16) + | ((*next_in)[2] << 8) + | ((*next_in)[3] ); + + if (box->size < 8) + return 0; + + if (*avail_in < box->size) + return 0; + + memcpy (box->type, *next_in + 4, 4); + box->ptr = *next_in + 8; + + *next_in += box->size; + *avail_in -= box->size; + + box->size -= 8; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////// + MODULE = Gtk2::CV PACKAGE = Gtk2::CV PROTOTYPES: ENABLE @@ -371,12 +419,18 @@ { STRLEN data_len; U8 *data = SvPVbyte (image_data, data_len); + static const unsigned char jxl_header[] = { + 0, 0, 0, 0x0c, 0x4a, 0x58, 0x4c, 0x20, + 0x0d, 0xa, 0x87, 0x0a, 0, 0, 0, 0x14, + 0x66, 0x74, 0x79, 0x70, 0x6a, 0x78, 0x6c, 0x20, + 0, 0, 0, 0, 0x6a, 0x78, 0x6c, 0x20, + }; if (data_len >= 20 && data[0] == 0xff && data[1] == 0xd8 && data[2] == 0xff) - RETVAL = "jpg"; + RETVAL = "image/jpeg"; else if (data_len >= 12 && data[ 0] == (U8)'R' && data[ 1] == (U8)'I' @@ -386,7 +440,7 @@ && data[ 9] == (U8)'E' && data[10] == (U8)'B' && data[11] == (U8)'P') - RETVAL = "webp"; + RETVAL = "image/webp"; else if (data_len >= 16 && data[ 0] == 0x89 && data[ 1] == (U8)'P' @@ -396,9 +450,11 @@ && data[ 5] == 0x0a && data[ 6] == 0x1a && data[ 7] == 0x0a) - RETVAL = "png"; + RETVAL = "image/png"; + else if (data_len >= sizeof (jxl_header) && memcmp (data, jxl_header, sizeof (jxl_header)) == 0) + RETVAL = "image/jxl"; else - RETVAL = "other"; + XSRETURN_UNDEF; } OUTPUT: RETVAL @@ -493,6 +549,114 @@ RETVAL GdkPixbuf_noinc * +decode_jxl (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0) + CODE: +{ +#if JXL + JxlDecoder *dec = JxlDecoderCreate (0); + JxlBasicInfo info; + const uint8_t *next_in = (uint8_t *)SvPVbyte_nolen (image_data); + size_t avail_in = SvCUR (image_data); + const char *error = 0; + void *runner = 0; + struct bmff_box box; + + perlinterp_release (); + + RETVAL = 0; + + error = "JxlDecoderCreate failed"; + if (!dec) + goto done; + + runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ()); + + error = "JxlDecoderSetParallelRunner failed"; + if (JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS) + goto done; + + error = "JxlDecoderSubscribeEvents failed"; + if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | 0xffc0) != JXL_DEC_SUCCESS) + goto done; + + for (;;) + { + JxlDecoderStatus status = JxlDecoderProcessInput (dec, &next_in, &avail_in); + + printf ("status %d\n",status); + + switch (status) + { + case JXL_DEC_ERROR: + error = "JxlDecoderProcessInput failed"; + goto done; + + case JXL_DEC_NEED_MORE_INPUT: + error = "incomplete file"; + goto done; + + case JXL_DEC_SUCCESS: + goto done; + + case JXL_DEC_NEED_IMAGE_OUT_BUFFER: + { + error = "JxlDecoderGetBasicInfo failed"; + if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS) + goto done; + + RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize); + error = "unable to allocate pixbuf"; + if (!RETVAL) + goto done; + + JxlPixelFormat format = { + info.alpha_bits ? 4 : 3, + JXL_TYPE_UINT8, + JXL_NATIVE_ENDIAN, + gdk_pixbuf_get_rowstride (RETVAL) + }; + + error = "JxlDecoderSetImageOutBuffer failed"; + if (JxlDecoderSetImageOutBuffer ( + dec, + &format, + gdk_pixbuf_get_pixels (RETVAL), + gdk_pixbuf_get_byte_length (RETVAL) + ) != JXL_DEC_SUCCESS) + goto done; + } + break; + + default: + error = "unexpected event"; + goto done; + } + } + + done: + if (dec) + JxlDecoderDestroy (dec); + + if (runner) + JxlThreadParallelRunnerDestroy (runner); + + perlinterp_acquire (); + + if (error) + { + if (RETVAL) + g_object_unref (RETVAL); + + croak ("load_jxl: %s", error); + } +#else + croak ("load_jxl: jpeg-xl not enabled at compile time"); +#endif +} + OUTPUT: + RETVAL + +GdkPixbuf_noinc * decode_jpeg (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0) CODE: { @@ -523,6 +687,9 @@ XSRETURN_UNDEF; } + if (!data_len) + longjmp (jerr.setjmp_buffer, 4); + jpeg_create_decompress (&cinfo); cv_jpeg_mem_src (&cinfo, data, data_len);