--- CV/CV.xs 2018/07/30 22:55:46 1.57 +++ CV/CV.xs 2021/11/28 23:26:51 1.63 @@ -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" @@ -259,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 @@ -377,12 +419,15 @@ { 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 + }; 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' @@ -392,7 +437,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' @@ -402,9 +447,46 @@ && 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"; // todo: might want to use JxlSignatureCheck + else if (data_len >= 2 + && data[0] == 0xff + && data[1] == 0x0a) + RETVAL = "image/jxl"; + else if (data_len >= 13 + && data[0] == 'G' + && data[1] == 'I' + && data[2] == 'F' + && data[3] == '8' + //&& (data[4] == '7' || data[4] == '9') + && data[5] == 'a') + { + RETVAL = "image/gif"; + + // now see if its animated - we require the netscape application header for this + int ofs = 13; + + if (data[10] & 0x80) + ofs += (1 << ((data[10] & 7) + 1)) * 3; + + if (data_len >= ofs + 2 + 1 + 11) + { + + // skip a graphic control extension block. we assume + // there is at most one such block - while the NAB + // has to come firstz, some files do not obey this + if (data[ofs] == 0x21 && data[ofs + 1] == 0xf9) + ofs += 3 + data[ofs + 2] + 1; + + if (data_len >= ofs + 2 + 1 + 11) + if (!memcmp (data + ofs, "\x21\xff\x0bNETSCAPE2.0", sizeof ("\x21\xff\x0bNETSCAPE2.0") - 1)) + RETVAL = "video/gif"; + } + } + else - RETVAL = "other"; + XSRETURN_UNDEF; } OUTPUT: RETVAL @@ -499,6 +581,135 @@ RETVAL GdkPixbuf_noinc * +decode_jxl (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0) + CODE: +{ +#if JXL + JxlDecoder *dec; + JxlBasicInfo info; + const uint8_t *next_in = (uint8_t *)SvPVbyte_nolen (image_data); + size_t avail_in = SvCUR (image_data); + const char *error = 0; + JxlDecoderStatus status; + void *runner = 0; + struct bmff_box box; + + RETVAL = 0; + + perlinterp_release (); + + dec = JxlDecoderCreate (0); + + error = "JxlDecoderCreate failed"; + if (!dec) + goto done; + + runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ()); + + status = JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner); + error = "JxlDecoderSetParallelRunner failed"; + if (status != JXL_DEC_SUCCESS) + goto done; + + error = "JxlDecoderSubscribeEvents failed"; + status = JxlDecoderSubscribeEvents (dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE); + if (status != JXL_DEC_SUCCESS) + goto done; + + status = JxlDecoderSetInput (dec, next_in, avail_in); + error = "JxlDecoderSetInput failed"; + if (status != JXL_DEC_SUCCESS) + goto done; + + for (;;) + { + status = JxlDecoderProcessInput (dec); + + switch (status) + { + case JXL_DEC_FULL_IMAGE: + error = 0; + goto done; + + case JXL_DEC_ERROR: + error = "JxlDecoderProcessInput failed"; + goto done; + + case JXL_DEC_NEED_MORE_INPUT: + error = "incomplete file"; + goto done; + + case JXL_DEC_SUCCESS: + error = "incomplete decode"; + goto done; + + case JXL_DEC_BASIC_INFO: + { + status = JxlDecoderGetBasicInfo (dec, &info); + error = "JxlDecoderGetBasicInfo failed"; + if (status != 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) + }; + + // cannot use gdk_pixbuf_get_byte_length because that does + // not return the size of the buffer, but the size of the buffer without + // the last padding bytes. the internal buffer is rowstride * ysize, + // and this is what the jxl decoder needs. none of this is documented + // in either library, of course. + + status = JxlDecoderSetImageOutBuffer ( + dec, + &format, + gdk_pixbuf_get_pixels (RETVAL), + gdk_pixbuf_get_rowstride (RETVAL) * info.ysize + ); + error = "JxlDecoderSetImageOutBuffer failed"; + if (status != 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 (status %d)", error, status); + } +#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: { @@ -691,7 +902,7 @@ STRLEN plen; U8 *path = (U8 *)SvPV (pathsv, plen); U8 *pend = path + plen; - U8 dst [plen * 6 * 3], *dstp = dst; + U8 dst [plen * 8 * 3], *dstp = dst; while (path < pend) { @@ -703,11 +914,12 @@ *dstp++ = *path++ + ('a' - 'A'); else if (ch >= '0' && ch <= '9') { + /* version sort, up to 8 digits */ STRLEN el, nl = 0; while (*path >= '0' && *path <= '9' && path < pend) path++, nl++; - for (el = nl; el < 6; el++) + for (el = nl; el < 8; el++) *dstp++ = '0'; memcpy (dstp, path - nl, nl);