ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/CV.xs
(Generate patch)

Comparing CV/CV.xs (file contents):
Revision 1.51 by root, Sat Dec 23 04:11:49 2017 UTC vs.
Revision 1.62 by root, Sun Jul 25 11:32:46 2021 UTC

7#include <math.h> 7#include <math.h>
8 8
9#include <magic.h> 9#include <magic.h>
10 10
11#include <jpeglib.h> 11#include <jpeglib.h>
12#include <jerror.h>
13
12#include <glib.h> 14#include <glib.h>
13#include <gtk/gtk.h> 15#include <gtk/gtk.h>
14#include <gdk/gdkx.h> 16#include <gdk/gdkx.h>
15#include <gdk-pixbuf/gdk-pixbuf.h> 17#include <gdk-pixbuf/gdk-pixbuf.h>
16 18
18#include <gtk2perl.h> 20#include <gtk2perl.h>
19 21
20#include <assert.h> 22#include <assert.h>
21 23
22#if WEBP 24#if WEBP
25#include <webp/demux.h>
23#include <webp/decode.h> 26#include <webp/decode.h>
24#endif 27#endif
25 28
29#if JXL
30#include <jxl/decode.h>
31#include "jxl/thread_parallel_runner.h"
32#endif
33
26#include "perlmulticore.h" 34#include "perlmulticore.h"
27 35
28#define IW 80 /* MUST match Schnauzer.pm! */ 36#define IW 80 /* MUST match Schnauzer.pm! */
29#define IH 60 /* MUST match Schnauzer.pm! */ 37#define IH 60 /* MUST match Schnauzer.pm! */
30
31#define RAND (seed = (seed + 7141) * 54773 % 134456)
32 38
33#define LINELENGTH 240 39#define LINELENGTH 240
34 40
35#define ELLIPSIS "\xe2\x80\xa6" 41#define ELLIPSIS "\xe2\x80\xa6"
36 42
198 204
199 PerlIO_write (fp, a85_buf, a85_ptr - a85_buf); 205 PerlIO_write (fp, a85_buf, a85_ptr - a85_buf);
200} 206}
201 207
202///////////////////////////////////////////////////////////////////////////// 208/////////////////////////////////////////////////////////////////////////////
209// memory source for libjpeg
210
211static void cv_ms_init (j_decompress_ptr cinfo)
212{
213}
214
215static void cv_ms_term (j_decompress_ptr cinfo)
216{
217}
218
219static boolean cv_ms_fill (j_decompress_ptr cinfo)
220{
221 // unexpected EOF, warn and generate fake EOI marker
222
223 WARNMS (cinfo, JWRN_JPEG_EOF);
224
225 struct jpeg_source_mgr *src = (struct jpeg_source_mgr *)cinfo->src;
226
227 static const JOCTET eoi[] = { 0xFF, JPEG_EOI };
228
229 src->next_input_byte = eoi;
230 src->bytes_in_buffer = sizeof (eoi);
231
232 return TRUE;
233}
234
235static void cv_ms_skip (j_decompress_ptr cinfo, long num_bytes)
236{
237 struct jpeg_source_mgr *src = (struct jpeg_source_mgr *)cinfo->src;
238
239 src->next_input_byte += num_bytes;
240 src->bytes_in_buffer -= num_bytes;
241}
242
243static void cv_jpeg_mem_src (j_decompress_ptr cinfo, void *buf, size_t buflen)
244{
245 struct jpeg_source_mgr *src;
246
247 if (!cinfo->src)
248 cinfo->src = (struct jpeg_source_mgr *)
249 (*cinfo->mem->alloc_small) (
250 (j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (struct jpeg_source_mgr)
251 );
252
253 src = (struct jpeg_source_mgr *)cinfo->src;
254 src->init_source = cv_ms_init;
255 src->fill_input_buffer = cv_ms_fill;
256 src->skip_input_data = cv_ms_skip;
257 src->resync_to_restart = jpeg_resync_to_restart;
258 src->term_source = cv_ms_term;
259 src->next_input_byte = (JOCTET *)buf;
260 src->bytes_in_buffer = buflen;
261}
262
263/////////////////////////////////////////////////////////////////////////////
264
265/* great, the jpeg-xl reference implementaton requires us to parse bmff files */
266
267struct bmff_box
268{
269 char type[4];
270 const uint8_t *ptr;
271 size_t size;
272};
273
274static int
275bmff_parse_box (struct bmff_box *box, const uint8_t **next_in, size_t *avail_in)
276{
277 if (*avail_in < 8)
278 return 0;
279
280 box->size = ((*next_in)[0] << 24)
281 | ((*next_in)[1] << 16)
282 | ((*next_in)[2] << 8)
283 | ((*next_in)[3] );
284
285 if (box->size < 8)
286 return 0;
287
288 if (*avail_in < box->size)
289 return 0;
290
291 memcpy (box->type, *next_in + 4, 4);
292 box->ptr = *next_in + 8;
293
294 *next_in += box->size;
295 *avail_in -= box->size;
296
297 box->size -= 8;
298
299 return 1;
300}
301
302/////////////////////////////////////////////////////////////////////////////
203 303
204MODULE = Gtk2::CV PACKAGE = Gtk2::CV 304MODULE = Gtk2::CV PACKAGE = Gtk2::CV
205 305
206PROTOTYPES: ENABLE 306PROTOTYPES: ENABLE
207 307
308# calculate the common prefix length of two strings
208# missing function in perl. really :) 309# missing function in perl. really :)
209int 310int
210common_prefix_length (a, b) 311common_prefix_length (a, b)
211 unsigned char *a = (unsigned char *)SvPVutf8_nolen ($arg); 312 unsigned char *a = (unsigned char *)SvPVutf8_nolen ($arg);
212 unsigned char *b = (unsigned char *)SvPVutf8_nolen ($arg); 313 unsigned char *b = (unsigned char *)SvPVutf8_nolen ($arg);
232 CODE: 333 CODE:
233{ 334{
234 STRLEN len; 335 STRLEN len;
235 char *data = SvPVbyte (path_or_data, len); 336 char *data = SvPVbyte (path_or_data, len);
236 337
237 perlinterp_release ();
238
239 if (!magic_cookie[0]) 338 if (!magic_cookie[0])
240 { 339 {
241 magic_cookie[0] = magic_open (MAGIC_SYMLINK); 340 magic_cookie[0] = magic_open (MAGIC_SYMLINK);
242 magic_cookie[1] = magic_open (MAGIC_SYMLINK | MAGIC_MIME); 341 magic_cookie[1] = magic_open (MAGIC_SYMLINK | MAGIC_MIME_TYPE);
342 magic_load (magic_cookie[0], 0);
343 magic_load (magic_cookie[1], 0);
243 } 344 }
345
346 perlinterp_release ();
244 347
245 RETVAL = ix & 2 348 RETVAL = ix & 2
246 ? magic_buffer (magic_cookie[ix], data, len) 349 ? magic_buffer (magic_cookie[ix & 1], data, len)
247 : magic_file (magic_cookie[ix], data); 350 : magic_file (magic_cookie[ix & 1], data);
248 351
249 perlinterp_acquire (); 352 perlinterp_acquire ();
250} 353}
251 OUTPUT: 354 OUTPUT:
252 RETVAL 355 RETVAL
308 : angle); 411 : angle);
309 perlinterp_acquire (); 412 perlinterp_acquire ();
310 OUTPUT: 413 OUTPUT:
311 RETVAL 414 RETVAL
312 415
416const char *
417filetype (SV *image_data)
418 CODE:
419{
420 STRLEN data_len;
421 U8 *data = SvPVbyte (image_data, data_len);
422 static const unsigned char jxl_header[] = {
423 0, 0, 0, 0x0c, 0x4a, 0x58, 0x4c, 0x20,
424 0x0d, 0xa, 0x87, 0x0a, 0, 0, 0, 0x14,
425 0x66, 0x74, 0x79, 0x70, 0x6a, 0x78, 0x6c, 0x20,
426 0, 0, 0, 0, 0x6a, 0x78, 0x6c, 0x20,
427 };
428
429 if (data_len >= 20
430 && data[0] == 0xff
431 && data[1] == 0xd8
432 && data[2] == 0xff)
433 RETVAL = "image/jpeg";
434 else if (data_len >= 12
435 && data[ 0] == (U8)'R'
436 && data[ 1] == (U8)'I'
437 && data[ 2] == (U8)'F'
438 && data[ 3] == (U8)'F'
439 && data[ 8] == (U8)'W'
440 && data[ 9] == (U8)'E'
441 && data[10] == (U8)'B'
442 && data[11] == (U8)'P')
443 RETVAL = "image/webp";
444 else if (data_len >= 16
445 && data[ 0] == 0x89
446 && data[ 1] == (U8)'P'
447 && data[ 2] == (U8)'N'
448 && data[ 3] == (U8)'G'
449 && data[ 4] == 0x0d
450 && data[ 5] == 0x0a
451 && data[ 6] == 0x1a
452 && data[ 7] == 0x0a)
453 RETVAL = "image/png";
454 else if (data_len >= sizeof (jxl_header) && memcmp (data, jxl_header, sizeof (jxl_header)) == 0)
455 RETVAL = "image/jxl";
456 else if (data_len >= 13
457 && data[0] == 'G'
458 && data[1] == 'I'
459 && data[2] == 'F'
460 && data[3] == '8'
461 //&& (data[4] == '7' || data[4] == '9')
462 && data[5] == 'a')
463 {
464 RETVAL = "image/gif";
465
466 // now see if its animated - we require the netscape application header for this
467 int ofs = 13;
468
469 if (data[10] & 0x80)
470 ofs += (1 << ((data[10] & 7) + 1)) * 3;
471
472 if (data_len >= ofs + 2 + 1 + 11)
473 {
474
475 // skip a graphic control extension block. we assume
476 // there is at most one such block - while the NAB
477 // has to come firstz, some files do not obey this
478 if (data[ofs] == 0x21 && data[ofs + 1] == 0xf9)
479 ofs += 3 + data[ofs + 2] + 1;
480
481 if (data_len >= ofs + 2 + 1 + 11)
482 if (!memcmp (data + ofs, "\x21\xff\x0bNETSCAPE2.0", sizeof ("\x21\xff\x0bNETSCAPE2.0") - 1))
483 RETVAL = "video/gif";
484 }
485 }
486
487 else
488 XSRETURN_UNDEF;
489}
490 OUTPUT:
491 RETVAL
492
313GdkPixbuf_noinc * 493GdkPixbuf_noinc *
314decode_webp (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0) 494decode_webp (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
315 CODE: 495 CODE:
316{ 496{
317#if WEBP 497#if WEBP
318 guchar *data;
319 STRLEN data_size; 498 STRLEN data_size;
320 int alpha; 499 int alpha;
500 WebPData data;
501 WebPDemuxer *demux;
502 WebPIterator iter;
321 WebPDecoderConfig config; 503 WebPDecoderConfig config;
322 int inw, inh; 504 int inw, inh;
323 505
324 data = SvPVbyte (image_data, data_size); 506 data.bytes = (uint8_t *)SvPVbyte (image_data, data_size);
507 data.size = data_size;
325 508
326 perlinterp_release (); 509 perlinterp_release ();
327 510
328 RETVAL = 0; 511 RETVAL = 0;
329 512
513 if (!(demux = WebPDemux (&data)))
514 goto err_demux;
515
516 if (!WebPDemuxGetFrame (demux, 1, &iter))
517 goto err_iter;
518
330 if (!WebPInitDecoderConfig (&config)) 519 if (!WebPInitDecoderConfig (&config))
331 goto err; 520 goto err_iter;
332 521
333 config.options.use_threads = 1; 522 config.options.use_threads = 1;
334 523
335 if (WebPGetFeatures (data, data_size, &config.input) != VP8_STATUS_OK) 524 if (WebPGetFeatures (iter.fragment.bytes, iter.fragment.size, &config.input) != VP8_STATUS_OK)
336 goto err; 525 goto err_iter;
337 526
338 inw = config.input.width; 527 inw = config.input.width;
339 inh = config.input.height; 528 inh = config.input.height;
340 529
341 if (thumbnail) 530 if (thumbnail)
356 { 545 {
357 iw = inw; 546 iw = inw;
358 ih = inh; 547 ih = inh;
359 } 548 }
360 549
361 alpha = config.input.has_alpha; 550 alpha = !!config.input.has_alpha;
362 551
363 RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!alpha, 8, iw, ih); 552 RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, alpha, 8, iw, ih);
364 if (!RETVAL) 553 if (!RETVAL)
365 goto err; 554 goto err_iter;
366 555
367 config.output.colorspace = alpha ? MODE_RGBA : MODE_RGB; 556 config.output.colorspace = alpha ? MODE_RGBA : MODE_RGB;
368 config.output.u.RGBA.rgba = gdk_pixbuf_get_pixels (RETVAL); 557 config.output.u.RGBA.rgba = gdk_pixbuf_get_pixels (RETVAL);
369 config.output.u.RGBA.stride = gdk_pixbuf_get_rowstride (RETVAL); 558 config.output.u.RGBA.stride = gdk_pixbuf_get_rowstride (RETVAL);
370 config.output.u.RGBA.size = gdk_pixbuf_get_byte_length (RETVAL); 559 config.output.u.RGBA.size = gdk_pixbuf_get_byte_length (RETVAL);
371 config.output.is_external_memory = 1; 560 config.output.is_external_memory = 1;
372 561
373 if (WebPDecode (data, data_size, &config) != VP8_STATUS_OK) 562 if (WebPDecode (iter.fragment.bytes, iter.fragment.size, &config) != VP8_STATUS_OK)
374 { 563 {
375 g_object_unref (RETVAL); 564 g_object_unref (RETVAL);
376 RETVAL = 0; 565 RETVAL = 0;
377 goto err; 566 goto err_iter;
378 } 567 }
379 568
380 err: 569 err_iter:
570 WebPDemuxReleaseIterator (&iter);
571 err_demux:
572 WebPDemuxDelete (demux);
573
381 perlinterp_acquire (); 574 perlinterp_acquire ();
382#else 575#else
383 croak ("load_webp: webp not enabled at compile time"); 576 croak ("load_webp: webp not enabled at compile time");
384#endif 577#endif
385} 578}
386 OUTPUT: 579 OUTPUT:
387 RETVAL 580 RETVAL
388 581
389GdkPixbuf_noinc * 582GdkPixbuf_noinc *
583decode_jxl (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
584 CODE:
585{
586#if JXL
587 JxlDecoder *dec = JxlDecoderCreate (0);
588 JxlBasicInfo info;
589 const uint8_t *next_in = (uint8_t *)SvPVbyte_nolen (image_data);
590 size_t avail_in = SvCUR (image_data);
591 const char *error = 0;
592 void *runner = 0;
593 struct bmff_box box;
594
595 perlinterp_release ();
596
597 RETVAL = 0;
598
599 error = "JxlDecoderCreate failed";
600 if (!dec)
601 goto done;
602
603 runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ());
604
605 error = "JxlDecoderSetParallelRunner failed";
606 if (JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS)
607 goto done;
608
609 error = "JxlDecoderSubscribeEvents failed";
610 if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | JXL_DEC_BASIC_INFO) != JXL_DEC_SUCCESS)
611 goto done;
612
613 error = "JxlDecoderSetInput failed";
614 if (JxlDecoderSetInput (dec, next_in, avail_in) != JXL_DEC_SUCCESS)
615 goto done;
616
617 for (;;)
618 {
619 JxlDecoderStatus status = JxlDecoderProcessInput (dec);
620
621 printf ("status %d\n",status);
622
623 switch (status)
624 {
625 case JXL_DEC_ERROR:
626 error = "JxlDecoderProcessInput failed";
627 goto done;
628
629 case JXL_DEC_NEED_MORE_INPUT:
630 error = "incomplete file";
631 goto done;
632
633 case JXL_DEC_SUCCESS:
634 goto done;
635
636 case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
637 {
638 error = "JxlDecoderGetBasicInfo failed";
639 if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS)
640 goto done;
641
642 RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize);
643 error = "unable to allocate pixbuf";
644 if (!RETVAL)
645 goto done;
646
647 JxlPixelFormat format = {
648 info.alpha_bits ? 4 : 3,
649 JXL_TYPE_UINT8,
650 JXL_NATIVE_ENDIAN,
651 gdk_pixbuf_get_rowstride (RETVAL)
652 };
653
654 error = "JxlDecoderSetImageOutBuffer failed";
655 if (JxlDecoderSetImageOutBuffer (
656 dec,
657 &format,
658 gdk_pixbuf_get_pixels (RETVAL),
659 gdk_pixbuf_get_byte_length (RETVAL)
660 ) != JXL_DEC_SUCCESS)
661 goto done;
662 }
663 break;
664
665 default:
666 error = "unexpected event";
667 goto done;
668 }
669 }
670
671 done:
672 if (dec)
673 JxlDecoderDestroy (dec);
674
675 if (runner)
676 JxlThreadParallelRunnerDestroy (runner);
677
678 perlinterp_acquire ();
679
680 if (error)
681 {
682 if (RETVAL)
683 g_object_unref (RETVAL);
684
685 croak ("load_jxl: %s", error);
686 }
687#else
688 croak ("load_jxl: jpeg-xl not enabled at compile time");
689#endif
690}
691 OUTPUT:
692 RETVAL
693
694GdkPixbuf_noinc *
390load_jpeg (SV *path, int thumbnail = 0, int iw = 0, int ih = 0) 695decode_jpeg (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
391 CODE: 696 CODE:
392{ 697{
393 struct jpeg_decompress_struct cinfo; 698 struct jpeg_decompress_struct cinfo;
394 struct jpg_err_mgr jerr; 699 struct jpg_err_mgr jerr;
395 guchar *data;
396 int rs; 700 int rs;
397 FILE *fp;
398 volatile GdkPixbuf *pb = 0; 701 volatile GdkPixbuf *pb = 0;
702 STRLEN data_len;
703 guchar *data = SvPVbyte (image_data, data_len);
399 704
400 RETVAL = 0; 705 RETVAL = 0;
401
402 fp = fopen (SvPVbyte_nolen (path), "rb");
403
404 if (!fp)
405 XSRETURN_UNDEF;
406 706
407 perlinterp_release (); 707 perlinterp_release ();
408 708
409 cinfo.err = jpeg_std_error (&jerr.err); 709 cinfo.err = jpeg_std_error (&jerr.err);
410 710
411 jerr.err.error_exit = cv_error_exit; 711 jerr.err.error_exit = cv_error_exit;
412 jerr.err.output_message = cv_error_output; 712 jerr.err.output_message = cv_error_output;
413 713
414 if ((rs = setjmp (jerr.setjmp_buffer))) 714 if ((rs = setjmp (jerr.setjmp_buffer)))
415 { 715 {
416 fclose (fp);
417 jpeg_destroy_decompress (&cinfo); 716 jpeg_destroy_decompress (&cinfo);
418 717
419 if (pb) 718 if (pb)
420 g_object_unref ((gpointer)pb); 719 g_object_unref ((gpointer)pb);
421 720
422 perlinterp_acquire (); 721 perlinterp_acquire ();
423 XSRETURN_UNDEF; 722 XSRETURN_UNDEF;
424 } 723 }
425 724
725 if (!data_len)
726 longjmp (jerr.setjmp_buffer, 4);
727
426 jpeg_create_decompress (&cinfo); 728 jpeg_create_decompress (&cinfo);
729 cv_jpeg_mem_src (&cinfo, data, data_len);
427 730
428 jpeg_stdio_src (&cinfo, fp);
429 jpeg_read_header (&cinfo, TRUE); 731 jpeg_read_header (&cinfo, TRUE);
430 732
431 cinfo.dct_method = JDCT_DEFAULT; 733 cinfo.dct_method = JDCT_DEFAULT;
432 cinfo.do_fancy_upsampling = FALSE; /* worse quality, but nobody compained so far, and gdk-pixbuf does the same */ 734 cinfo.do_fancy_upsampling = FALSE; /* worse quality, but nobody compained so far, and gdk-pixbuf does the same */
433 cinfo.do_block_smoothing = FALSE; 735 cinfo.do_block_smoothing = FALSE;
512 data += 4; 814 data += 4;
513 } 815 }
514 } 816 }
515 817
516 jpeg_finish_decompress (&cinfo); 818 jpeg_finish_decompress (&cinfo);
517 fclose (fp);
518 jpeg_destroy_decompress (&cinfo); 819 jpeg_destroy_decompress (&cinfo);
519 perlinterp_acquire (); 820 perlinterp_acquire ();
520} 821}
521 OUTPUT: 822 OUTPUT:
522 RETVAL 823 RETVAL
581 CODE: 882 CODE:
582{ 883{
583 STRLEN plen; 884 STRLEN plen;
584 U8 *path = (U8 *)SvPV (pathsv, plen); 885 U8 *path = (U8 *)SvPV (pathsv, plen);
585 U8 *pend = path + plen; 886 U8 *pend = path + plen;
586 U8 dst [plen * 6 * 3], *dstp = dst; 887 U8 dst [plen * 8 * 3], *dstp = dst;
587 888
588 while (path < pend) 889 while (path < pend)
589 { 890 {
590 U8 ch = *path; 891 U8 ch = *path;
591 892
593 *dstp++ = *path++; 894 *dstp++ = *path++;
594 else if (ch >= 'A' && ch <= 'Z') 895 else if (ch >= 'A' && ch <= 'Z')
595 *dstp++ = *path++ + ('a' - 'A'); 896 *dstp++ = *path++ + ('a' - 'A');
596 else if (ch >= '0' && ch <= '9') 897 else if (ch >= '0' && ch <= '9')
597 { 898 {
899 /* version sort, up to 8 digits */
598 STRLEN el, nl = 0; 900 STRLEN el, nl = 0;
599 while (*path >= '0' && *path <= '9' && path < pend) 901 while (*path >= '0' && *path <= '9' && path < pend)
600 path++, nl++; 902 path++, nl++;
601 903
602 for (el = nl; el < 6; el++) 904 for (el = nl; el < 8; el++)
603 *dstp++ = '0'; 905 *dstp++ = '0';
604 906
605 memcpy (dstp, path - nl, nl); 907 memcpy (dstp, path - nl, nl);
606 dstp += nl; 908 dstp += nl;
607 } 909 }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines