--- CV/CV.xs 2021/04/10 03:32:09 1.59 +++ CV/CV.xs 2021/11/28 23:26:51 1.63 @@ -420,10 +420,7 @@ 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, + 0, 0, 0, 0x0c, 0x4a, 0x58, 0x4c, 0x20, 0x0d, 0xa, 0x87, 0x0a }; if (data_len >= 20 @@ -452,7 +449,42 @@ && data[ 7] == 0x0a) 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 XSRETURN_UNDEF; } @@ -553,17 +585,20 @@ CODE: { #if JXL - JxlDecoder *dec = JxlDecoderCreate (0); + 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 (); - RETVAL = 0; + dec = JxlDecoderCreate (0); error = "JxlDecoderCreate failed"; if (!dec) @@ -571,26 +606,31 @@ runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ()); + status = JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner); error = "JxlDecoderSetParallelRunner failed"; - if (JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS) + if (status != JXL_DEC_SUCCESS) goto done; error = "JxlDecoderSubscribeEvents failed"; - if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | JXL_DEC_BASIC_INFO) != JXL_DEC_SUCCESS) + 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 (JxlDecoderSetInput (dec, next_in, avail_in) != JXL_DEC_SUCCESS) + if (status != JXL_DEC_SUCCESS) goto done; for (;;) { - JxlDecoderStatus status = JxlDecoderProcessInput (dec); + status = JxlDecoderProcessInput (dec); - printf ("status %d\n",status); - switch (status) { + case JXL_DEC_FULL_IMAGE: + error = 0; + goto done; + case JXL_DEC_ERROR: error = "JxlDecoderProcessInput failed"; goto done; @@ -600,12 +640,14 @@ goto done; case JXL_DEC_SUCCESS: + error = "incomplete decode"; goto done; - case JXL_DEC_NEED_IMAGE_OUT_BUFFER: + case JXL_DEC_BASIC_INFO: { + status = JxlDecoderGetBasicInfo (dec, &info); error = "JxlDecoderGetBasicInfo failed"; - if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS) + if (status != JXL_DEC_SUCCESS) goto done; RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize); @@ -620,13 +662,20 @@ 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 (JxlDecoderSetImageOutBuffer ( - dec, - &format, - gdk_pixbuf_get_pixels (RETVAL), - gdk_pixbuf_get_byte_length (RETVAL) - ) != JXL_DEC_SUCCESS) + if (status != JXL_DEC_SUCCESS) goto done; } break; @@ -651,7 +700,7 @@ if (RETVAL) g_object_unref (RETVAL); - croak ("load_jxl: %s", error); + croak ("load_jxl: %s (status %d)", error, status); } #else croak ("load_jxl: jpeg-xl not enabled at compile time"); @@ -853,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) { @@ -865,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);