ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/CV.xs
Revision: 1.59
Committed: Sat Apr 10 03:32:09 2021 UTC (3 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-1_9
Changes since 1.58: +6 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #include "EXTERN.h"
2     #include "perl.h"
3     #include "XSUB.h"
4    
5 root 1.18 #include <string.h>
6 root 1.17 #include <setjmp.h>
7 root 1.33 #include <math.h>
8 root 1.17
9 root 1.31 #include <magic.h>
10    
11 root 1.17 #include <jpeglib.h>
12 root 1.53 #include <jerror.h>
13    
14 root 1.23 #include <glib.h>
15     #include <gtk/gtk.h>
16 root 1.50 #include <gdk/gdkx.h>
17 root 1.2 #include <gdk-pixbuf/gdk-pixbuf.h>
18    
19     #include <gperl.h>
20     #include <gtk2perl.h>
21    
22 root 1.43 #include <assert.h>
23    
24 root 1.50 #if WEBP
25 root 1.54 #include <webp/demux.h>
26 root 1.50 #include <webp/decode.h>
27     #endif
28    
29 root 1.58 #if JXL
30     #include <jxl/decode.h>
31     #include "jxl/thread_parallel_runner.h"
32     #endif
33    
34 root 1.46 #include "perlmulticore.h"
35    
36 root 1.33 #define IW 80 /* MUST match Schnauzer.pm! */
37     #define IH 60 /* MUST match Schnauzer.pm! */
38 root 1.5
39 root 1.7 #define LINELENGTH 240
40    
41 root 1.6 #define ELLIPSIS "\xe2\x80\xa6"
42    
43 root 1.42 typedef char *octet_string;
44    
45 root 1.51 static magic_t magic_cookie[2]; /* !mime, mime */
46    
47 root 1.17 struct jpg_err_mgr
48     {
49     struct jpeg_error_mgr err;
50     jmp_buf setjmp_buffer;
51     };
52    
53     static void
54     cv_error_exit (j_common_ptr cinfo)
55     {
56 root 1.48 cinfo->err->output_message (cinfo);
57 root 1.17 longjmp (((struct jpg_err_mgr *)cinfo->err)->setjmp_buffer, 99);
58     }
59    
60     static void
61     cv_error_output (j_common_ptr cinfo)
62     {
63 root 1.48 char msg[JMSG_LENGTH_MAX];
64    
65     cinfo->err->format_message (cinfo, msg);
66    
67     fprintf (stderr, "JPEG decoding error: %s\n", msg);
68 root 1.17 return;
69     }
70    
71     static void
72     rgb_to_hsv (unsigned int r, unsigned int g, unsigned int b,
73     unsigned int *h, unsigned int *s, unsigned int *v)
74     {
75     unsigned int mx = r; if (g > mx) mx = g; if (b > mx) mx = b;
76     unsigned int mn = r; if (g < mn) mn = g; if (b < mn) mn = b;
77     unsigned int delta = mx - mn;
78    
79     *v = mx;
80    
81     *s = mx ? delta * 255 / mx : 0;
82    
83     if (delta == 0)
84     *h = 0;
85     else
86     {
87     if (r == mx)
88     *h = ((int)g - (int)b) * 255 / (int)(delta * 3);
89     else if (g == mx)
90     *h = ((int)b - (int)r) * 255 / (int)(delta * 3) + 52;
91     else if (b == mx)
92     *h = ((int)r - (int)g) * 255 / (int)(delta * 3) + 103;
93    
94     *h &= 255;
95     }
96     }
97    
98 root 1.36 struct feature {
99     float v1, v2, v3; // mean, square, cube
100     int n;
101     };
102    
103     static void
104     feature_init (struct feature *f)
105     {
106     f->v1 = 0.;
107     f->v2 = 0.;
108     f->v3 = 0.;
109     f->n = 0;
110     }
111    
112     // didn't find an algorithm to neatly do mean, variance and skew in one pass.
113     // elmex ist schuld.
114     static void
115     feature_update_pass_1 (struct feature *f, unsigned int v)
116     {
117     f->v1 += v;
118     f->n += 1;
119     }
120    
121     static void
122     feature_finish_pass_1 (struct feature *f)
123     {
124     if (f->n < 1)
125     return;
126    
127     f->v1 /= f->n;
128     }
129    
130     static void
131     feature_update_pass_2 (struct feature *f, unsigned int v)
132     {
133     float d = v - f->v1;
134    
135     f->v2 += d * d;
136     f->v3 += d * d * d;
137     }
138    
139     static void
140     feature_finish_pass_2 (struct feature *f)
141     {
142     if (f->n < 1)
143     return;
144    
145     f->v2 /= f->n;
146     f->v3 /= f->n;
147    
148     f->v1 /= 255.;
149 root 1.37 f->v2 /= 255. * 255.; f->v2 = sqrtf (f->v2);
150     f->v3 /= 255. * 255. * 255.; f->v3 = powf (fabsf (f->v3), 1./3.);
151 root 1.36 }
152    
153 root 1.4 static guint32 a85_val;
154     static guint a85_cnt;
155 root 1.7 static guchar a85_buf[LINELENGTH], *a85_ptr;
156 root 1.4
157     static void
158     a85_init (void)
159     {
160     a85_cnt = 4;
161     a85_ptr = a85_buf;
162     }
163    
164     static void
165     a85_push (PerlIO *fp, guchar c)
166     {
167     a85_val = a85_val << 8 | c;
168    
169     if (!--a85_cnt)
170     {
171     a85_cnt = 4;
172     if (a85_val)
173     {
174 root 1.49 a85_ptr[4] = (a85_val % 85) + 33; a85_val /= 85;
175     a85_ptr[3] = (a85_val % 85) + 33; a85_val /= 85;
176     a85_ptr[2] = (a85_val % 85) + 33; a85_val /= 85;
177 root 1.4 a85_ptr[1] = (a85_val % 85) + 33; a85_val /= 85;
178     a85_ptr[0] = (a85_val ) + 33;
179    
180     a85_ptr += 5;
181     }
182     else
183     *a85_ptr++ = 'z';
184    
185     if (a85_ptr >= a85_buf + sizeof (a85_buf) - 7)
186     {
187     *a85_ptr++ = '\n';
188     PerlIO_write (fp, a85_buf, a85_ptr - a85_buf);
189     a85_ptr = a85_buf;
190     }
191     }
192    
193     }
194    
195 root 1.8 static void
196 root 1.4 a85_finish (PerlIO *fp)
197     {
198     while (a85_cnt != 4)
199     a85_push (fp, 0);
200    
201     *a85_ptr++ = '~'; // probably buggy end-marker
202     *a85_ptr++ = '>'; // probably buggy end-marker
203     *a85_ptr++ = '\n';
204    
205     PerlIO_write (fp, a85_buf, a85_ptr - a85_buf);
206     }
207    
208 root 1.6 /////////////////////////////////////////////////////////////////////////////
209 root 1.56 // memory source for libjpeg
210 root 1.6
211 root 1.53 static void cv_ms_init (j_decompress_ptr cinfo)
212     {
213     }
214    
215     static void cv_ms_term (j_decompress_ptr cinfo)
216     {
217     }
218    
219     static boolean cv_ms_fill (j_decompress_ptr cinfo)
220     {
221 root 1.57 // 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 root 1.53
232     return TRUE;
233     }
234    
235     static void cv_ms_skip (j_decompress_ptr cinfo, long num_bytes)
236     {
237 root 1.57 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 root 1.53 }
242    
243     static 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 root 1.58 /* great, the jpeg-xl reference implementaton requires us to parse bmff files */
266    
267     struct bmff_box
268     {
269     char type[4];
270     const uint8_t *ptr;
271     size_t size;
272     };
273    
274     static int
275     bmff_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     /////////////////////////////////////////////////////////////////////////////
303    
304 root 1.9 MODULE = Gtk2::CV PACKAGE = Gtk2::CV
305 root 1.1
306     PROTOTYPES: ENABLE
307 root 1.2
308 root 1.56 # calculate the common prefix length of two strings
309 root 1.29 # missing function in perl. really :)
310     int
311     common_prefix_length (a, b)
312     unsigned char *a = (unsigned char *)SvPVutf8_nolen ($arg);
313     unsigned char *b = (unsigned char *)SvPVutf8_nolen ($arg);
314     CODE:
315     RETVAL = 0;
316    
317     while (*a == *b && *a)
318     {
319     RETVAL += (*a & 0xc0) != 0x80;
320     a++, b++;
321     }
322    
323     OUTPUT:
324     RETVAL
325    
326 root 1.31 const char *
327 root 1.51 magic (SV *path_or_data)
328     ALIAS:
329     magic = 0
330     magic_mime = 1
331     magic_buffer = 2
332     magic_buffer_mime = 3
333 root 1.31 CODE:
334     {
335 root 1.51 STRLEN len;
336     char *data = SvPVbyte (path_or_data, len);
337 root 1.31
338 root 1.51 if (!magic_cookie[0])
339     {
340     magic_cookie[0] = magic_open (MAGIC_SYMLINK);
341 root 1.52 magic_cookie[1] = magic_open (MAGIC_SYMLINK | MAGIC_MIME_TYPE);
342     magic_load (magic_cookie[0], 0);
343     magic_load (magic_cookie[1], 0);
344 root 1.32 }
345    
346 root 1.52 perlinterp_release ();
347    
348 root 1.51 RETVAL = ix & 2
349 root 1.52 ? magic_buffer (magic_cookie[ix & 1], data, len)
350     : magic_file (magic_cookie[ix & 1], data);
351 root 1.32
352 root 1.46 perlinterp_acquire ();
353 root 1.31 }
354     OUTPUT:
355     RETVAL
356    
357 root 1.40 # missing/broken in Gtk2 perl module
358    
359     void
360     gdk_window_clear_hints (GdkWindow *window)
361     CODE:
362     gdk_window_set_geometry_hints (window, 0, 0);
363 root 1.22
364     gboolean
365     gdk_net_wm_supports (GdkAtom property)
366     CODE:
367     #if defined(GDK_WINDOWING_X11) && !defined(GDK_MULTIHEAD_SAFE)
368     RETVAL = gdk_net_wm_supports (property);
369     #else
370     RETVAL = 0;
371     #endif
372     OUTPUT:
373     RETVAL
374    
375 root 1.3 GdkPixbuf_noinc *
376 root 1.26 dealpha_expose (GdkPixbuf *pb)
377     CODE:
378 root 1.46 perlinterp_release ();
379 root 1.26 {
380     int w = gdk_pixbuf_get_width (pb);
381     int h = gdk_pixbuf_get_height (pb);
382     int bpp = gdk_pixbuf_get_n_channels (pb);
383     int x, y, i;
384     guchar *src = gdk_pixbuf_get_pixels (pb), *dst;
385     int sstr = gdk_pixbuf_get_rowstride (pb), dstr;
386    
387     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, w, h);
388    
389     dst = gdk_pixbuf_get_pixels (RETVAL);
390     dstr = gdk_pixbuf_get_rowstride (RETVAL);
391    
392     for (x = 0; x < w; x++)
393     for (y = 0; y < h; y++)
394     for (i = 0; i < 3; i++)
395     dst[x * 3 + y * dstr + i] = src[x * bpp + y * sstr + i];
396     }
397 root 1.46 perlinterp_acquire ();
398 root 1.26 OUTPUT:
399     RETVAL
400    
401     GdkPixbuf_noinc *
402 root 1.28 rotate (GdkPixbuf *pb, int angle)
403 root 1.2 CODE:
404 root 1.46 perlinterp_release ();
405 root 1.44 if (angle < 0)
406     angle += 360;
407 root 1.28 RETVAL = gdk_pixbuf_rotate_simple (pb, angle == 0 ? GDK_PIXBUF_ROTATE_NONE
408     : angle == 90 ? GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE
409     : angle == 180 ? GDK_PIXBUF_ROTATE_UPSIDEDOWN
410     : angle == 270 ? GDK_PIXBUF_ROTATE_CLOCKWISE
411     : angle);
412 root 1.46 perlinterp_acquire ();
413 root 1.2 OUTPUT:
414     RETVAL
415    
416 root 1.53 const char *
417     filetype (SV *image_data)
418     CODE:
419     {
420     STRLEN data_len;
421     U8 *data = SvPVbyte (image_data, data_len);
422 root 1.58 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 root 1.53
429     if (data_len >= 20
430     && data[0] == 0xff
431     && data[1] == 0xd8
432     && data[2] == 0xff)
433 root 1.58 RETVAL = "image/jpeg";
434 root 1.53 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 root 1.58 RETVAL = "image/webp";
444 root 1.53 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 root 1.58 RETVAL = "image/png";
454     else if (data_len >= sizeof (jxl_header) && memcmp (data, jxl_header, sizeof (jxl_header)) == 0)
455     RETVAL = "image/jxl";
456 root 1.53 else
457 root 1.58 XSRETURN_UNDEF;
458 root 1.53 }
459     OUTPUT:
460     RETVAL
461    
462 root 1.17 GdkPixbuf_noinc *
463 root 1.51 decode_webp (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
464 root 1.50 CODE:
465     {
466     #if WEBP
467     STRLEN data_size;
468     int alpha;
469 root 1.54 WebPData data;
470     WebPDemuxer *demux;
471     WebPIterator iter;
472     WebPDecoderConfig config;
473 root 1.51 int inw, inh;
474    
475 root 1.54 data.bytes = (uint8_t *)SvPVbyte (image_data, data_size);
476     data.size = data_size;
477 root 1.50
478     perlinterp_release ();
479    
480     RETVAL = 0;
481    
482 root 1.54 if (!(demux = WebPDemux (&data)))
483     goto err_demux;
484    
485     if (!WebPDemuxGetFrame (demux, 1, &iter))
486     goto err_iter;
487    
488     if (!WebPInitDecoderConfig (&config))
489     goto err_iter;
490 root 1.50
491     config.options.use_threads = 1;
492    
493 root 1.54 if (WebPGetFeatures (iter.fragment.bytes, iter.fragment.size, &config.input) != VP8_STATUS_OK)
494     goto err_iter;
495 root 1.51
496     inw = config.input.width;
497     inh = config.input.height;
498    
499 root 1.50 if (thumbnail)
500     {
501 root 1.51 if (inw * ih > inh * iw)
502     ih = (iw * inh + inw - 1) / inw;
503     else
504     iw = (ih * inw + inh - 1) / inh;
505    
506 root 1.50 config.options.bypass_filtering = 1;
507     config.options.no_fancy_upsampling = 1;
508    
509     config.options.use_scaling = 1;
510     config.options.scaled_width = iw;
511     config.options.scaled_height = ih;
512     }
513     else
514     {
515 root 1.51 iw = inw;
516     ih = inh;
517 root 1.50 }
518    
519 root 1.54 alpha = !!config.input.has_alpha;
520 root 1.50
521 root 1.54 RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, alpha, 8, iw, ih);
522 root 1.50 if (!RETVAL)
523 root 1.54 goto err_iter;
524 root 1.50
525     config.output.colorspace = alpha ? MODE_RGBA : MODE_RGB;
526     config.output.u.RGBA.rgba = gdk_pixbuf_get_pixels (RETVAL);
527     config.output.u.RGBA.stride = gdk_pixbuf_get_rowstride (RETVAL);
528     config.output.u.RGBA.size = gdk_pixbuf_get_byte_length (RETVAL);
529     config.output.is_external_memory = 1;
530    
531 root 1.54 if (WebPDecode (iter.fragment.bytes, iter.fragment.size, &config) != VP8_STATUS_OK)
532 root 1.50 {
533     g_object_unref (RETVAL);
534     RETVAL = 0;
535 root 1.54 goto err_iter;
536 root 1.50 }
537    
538 root 1.54 err_iter:
539     WebPDemuxReleaseIterator (&iter);
540     err_demux:
541 root 1.55 WebPDemuxDelete (demux);
542 root 1.54
543 root 1.50 perlinterp_acquire ();
544     #else
545     croak ("load_webp: webp not enabled at compile time");
546     #endif
547     }
548     OUTPUT:
549     RETVAL
550    
551     GdkPixbuf_noinc *
552 root 1.58 decode_jxl (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
553     CODE:
554     {
555     #if JXL
556     JxlDecoder *dec = JxlDecoderCreate (0);
557     JxlBasicInfo info;
558     const uint8_t *next_in = (uint8_t *)SvPVbyte_nolen (image_data);
559     size_t avail_in = SvCUR (image_data);
560     const char *error = 0;
561     void *runner = 0;
562     struct bmff_box box;
563    
564     perlinterp_release ();
565    
566     RETVAL = 0;
567    
568     error = "JxlDecoderCreate failed";
569     if (!dec)
570     goto done;
571    
572     runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ());
573    
574     error = "JxlDecoderSetParallelRunner failed";
575     if (JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS)
576     goto done;
577    
578     error = "JxlDecoderSubscribeEvents failed";
579 root 1.59 if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | JXL_DEC_BASIC_INFO) != JXL_DEC_SUCCESS)
580     goto done;
581    
582     error = "JxlDecoderSetInput failed";
583     if (JxlDecoderSetInput (dec, next_in, avail_in) != JXL_DEC_SUCCESS)
584 root 1.58 goto done;
585    
586     for (;;)
587     {
588 root 1.59 JxlDecoderStatus status = JxlDecoderProcessInput (dec);
589 root 1.58
590     printf ("status %d\n",status);
591    
592     switch (status)
593     {
594     case JXL_DEC_ERROR:
595     error = "JxlDecoderProcessInput failed";
596     goto done;
597    
598     case JXL_DEC_NEED_MORE_INPUT:
599     error = "incomplete file";
600     goto done;
601    
602     case JXL_DEC_SUCCESS:
603     goto done;
604    
605     case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
606     {
607     error = "JxlDecoderGetBasicInfo failed";
608     if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS)
609     goto done;
610    
611     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize);
612     error = "unable to allocate pixbuf";
613     if (!RETVAL)
614     goto done;
615    
616     JxlPixelFormat format = {
617     info.alpha_bits ? 4 : 3,
618     JXL_TYPE_UINT8,
619     JXL_NATIVE_ENDIAN,
620     gdk_pixbuf_get_rowstride (RETVAL)
621     };
622    
623     error = "JxlDecoderSetImageOutBuffer failed";
624     if (JxlDecoderSetImageOutBuffer (
625     dec,
626     &format,
627     gdk_pixbuf_get_pixels (RETVAL),
628     gdk_pixbuf_get_byte_length (RETVAL)
629     ) != JXL_DEC_SUCCESS)
630     goto done;
631     }
632     break;
633    
634     default:
635     error = "unexpected event";
636     goto done;
637     }
638     }
639    
640     done:
641     if (dec)
642     JxlDecoderDestroy (dec);
643    
644     if (runner)
645     JxlThreadParallelRunnerDestroy (runner);
646    
647     perlinterp_acquire ();
648    
649     if (error)
650     {
651     if (RETVAL)
652     g_object_unref (RETVAL);
653    
654     croak ("load_jxl: %s", error);
655     }
656     #else
657     croak ("load_jxl: jpeg-xl not enabled at compile time");
658     #endif
659     }
660     OUTPUT:
661     RETVAL
662    
663     GdkPixbuf_noinc *
664 root 1.53 decode_jpeg (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
665 root 1.17 CODE:
666     {
667     struct jpeg_decompress_struct cinfo;
668     struct jpg_err_mgr jerr;
669     int rs;
670     volatile GdkPixbuf *pb = 0;
671 root 1.53 STRLEN data_len;
672     guchar *data = SvPVbyte (image_data, data_len);
673 root 1.23
674 root 1.17 RETVAL = 0;
675    
676 root 1.46 perlinterp_release ();
677    
678 root 1.17 cinfo.err = jpeg_std_error (&jerr.err);
679    
680     jerr.err.error_exit = cv_error_exit;
681     jerr.err.output_message = cv_error_output;
682    
683     if ((rs = setjmp (jerr.setjmp_buffer)))
684     {
685     jpeg_destroy_decompress (&cinfo);
686    
687     if (pb)
688     g_object_unref ((gpointer)pb);
689    
690 root 1.46 perlinterp_acquire ();
691 root 1.17 XSRETURN_UNDEF;
692     }
693    
694 root 1.57 if (!data_len)
695     longjmp (jerr.setjmp_buffer, 4);
696    
697 root 1.17 jpeg_create_decompress (&cinfo);
698 root 1.53 cv_jpeg_mem_src (&cinfo, data, data_len);
699 root 1.17
700     jpeg_read_header (&cinfo, TRUE);
701    
702     cinfo.dct_method = JDCT_DEFAULT;
703     cinfo.do_fancy_upsampling = FALSE; /* worse quality, but nobody compained so far, and gdk-pixbuf does the same */
704     cinfo.do_block_smoothing = FALSE;
705     cinfo.out_color_space = JCS_RGB;
706     cinfo.quantize_colors = FALSE;
707    
708     cinfo.scale_num = 1;
709     cinfo.scale_denom = 1;
710    
711     jpeg_calc_output_dimensions (&cinfo);
712    
713     if (thumbnail)
714     {
715     cinfo.dct_method = JDCT_FASTEST;
716     cinfo.do_fancy_upsampling = FALSE;
717    
718     while (cinfo.scale_denom < 8
719 root 1.47 && cinfo.output_width >= iw*4
720     && cinfo.output_height >= ih*4)
721 root 1.17 {
722     cinfo.scale_denom <<= 1;
723     jpeg_calc_output_dimensions (&cinfo);
724     }
725     }
726    
727 root 1.48 if (cinfo.output_components != 3)
728     longjmp (jerr.setjmp_buffer, 3);
729    
730     if (cinfo.jpeg_color_space == JCS_YCCK || cinfo.jpeg_color_space == JCS_CMYK)
731     {
732     cinfo.out_color_space = JCS_CMYK;
733     cinfo.output_components = 4;
734     }
735    
736     pb = RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo.output_components == 4, 8, cinfo.output_width, cinfo.output_height);
737 root 1.17 if (!RETVAL)
738     longjmp (jerr.setjmp_buffer, 2);
739    
740     data = gdk_pixbuf_get_pixels (RETVAL);
741     rs = gdk_pixbuf_get_rowstride (RETVAL);
742    
743     jpeg_start_decompress (&cinfo);
744    
745     while (cinfo.output_scanline < cinfo.output_height)
746     {
747     int remaining = cinfo.output_height - cinfo.output_scanline;
748     JSAMPROW rp[4];
749    
750     rp [0] = data + cinfo.output_scanline * rs;
751     rp [1] = (guchar *)rp [0] + rs;
752     rp [2] = (guchar *)rp [1] + rs;
753     rp [3] = (guchar *)rp [2] + rs;
754    
755     jpeg_read_scanlines (&cinfo, rp, remaining < 4 ? remaining : 4);
756     }
757    
758 root 1.48 if (cinfo.out_color_space == JCS_CMYK)
759     {
760     guchar *end = data + cinfo.output_height * rs;
761    
762     while (data < end)
763     {
764     U32 c = data [0];
765     U32 m = data [1];
766     U32 y = data [2];
767     U32 k = data [3];
768    
769 root 1.49 if (0)
770     if (cinfo.Adobe_transform == 2)
771     {
772     c ^= 0xff;
773     m ^= 0xff;
774     y ^= 0xff;
775     k ^= 0xff;
776     }
777    
778     data [0] = (c * k + 0x80) / 0xff;
779     data [1] = (m * k + 0x80) / 0xff;
780     data [2] = (y * k + 0x80) / 0xff;
781     data [3] = 0xff;
782 root 1.48
783     data += 4;
784     }
785     }
786    
787 root 1.17 jpeg_finish_decompress (&cinfo);
788     jpeg_destroy_decompress (&cinfo);
789 root 1.46 perlinterp_acquire ();
790 root 1.17 }
791     OUTPUT:
792     RETVAL
793    
794 root 1.33 void
795     compare (GdkPixbuf *a, GdkPixbuf *b)
796     PPCODE:
797 root 1.46 perlinterp_release ();
798 root 1.33 {
799     int w = gdk_pixbuf_get_width (a);
800     int h = gdk_pixbuf_get_height (a);
801 root 1.43
802 root 1.33 int sa = gdk_pixbuf_get_rowstride (a);
803     int sb = gdk_pixbuf_get_rowstride (b);
804    
805     guchar *pa = gdk_pixbuf_get_pixels (a);
806     guchar *pb = gdk_pixbuf_get_pixels (b);
807    
808     int x, y;
809    
810     assert (w == gdk_pixbuf_get_width (b));
811     assert (h == gdk_pixbuf_get_height (b));
812    
813     assert (gdk_pixbuf_get_n_channels (a) == 3);
814     assert (gdk_pixbuf_get_n_channels (b) == 3);
815    
816     double diff = 0.;
817     int peak = 0;
818    
819 root 1.35 if (w && h)
820 root 1.34 for (y = 0; y < h; y++)
821     {
822     guchar *pa_ = pa + y * sa;
823     guchar *pb_ = pb + y * sb;
824 root 1.33
825 root 1.34 for (x = 0; x < w; x++)
826     {
827     int d;
828    
829     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
830     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
831     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
832     }
833     }
834 root 1.33
835 root 1.46 perlinterp_acquire ();
836    
837 root 1.33 EXTEND (SP, 2);
838     PUSHs (sv_2mortal (newSVnv (sqrt (diff / (w * h * 3. * 255. * 255.)))));
839     PUSHs (sv_2mortal (newSVnv (peak / 255.)));
840     }
841    
842 root 1.6 #############################################################################
843    
844 root 1.2 MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Schnauzer
845    
846 root 1.41 # currently only works for filenames (octet strings)
847    
848 root 1.18 SV *
849     foldcase (SV *pathsv)
850     PROTOTYPE: $
851     CODE:
852     {
853     STRLEN plen;
854 root 1.41 U8 *path = (U8 *)SvPV (pathsv, plen);
855 root 1.18 U8 *pend = path + plen;
856     U8 dst [plen * 6 * 3], *dstp = dst;
857    
858     while (path < pend)
859     {
860 root 1.19 U8 ch = *path;
861    
862     if (ch >= 'a' && ch <= 'z')
863     *dstp++ = *path++;
864 root 1.41 else if (ch >= 'A' && ch <= 'Z')
865     *dstp++ = *path++ + ('a' - 'A');
866 root 1.19 else if (ch >= '0' && ch <= '9')
867 root 1.18 {
868     STRLEN el, nl = 0;
869 root 1.19 while (*path >= '0' && *path <= '9' && path < pend)
870 root 1.18 path++, nl++;
871    
872     for (el = nl; el < 6; el++)
873     *dstp++ = '0';
874    
875     memcpy (dstp, path - nl, nl);
876     dstp += nl;
877     }
878     else
879 root 1.41 *dstp++ = *path++;
880     #if 0
881     else
882 root 1.18 {
883     STRLEN cl;
884     to_utf8_fold (path, dstp, &cl);
885     dstp += cl;
886     path += is_utf8_char (path);
887     }
888 root 1.41 #endif
889 root 1.18 }
890    
891 root 1.25 RETVAL = newSVpvn ((const char *)dst, dstp - dst);
892 root 1.18 }
893     OUTPUT:
894     RETVAL
895    
896 root 1.3 GdkPixbuf_noinc *
897 root 1.24 p7_to_pb (int w, int h, SV *src_sv)
898 root 1.21 PROTOTYPE: @
899 root 1.2 CODE:
900     {
901     int x, y;
902     guchar *dst, *d;
903     int dstr;
904 root 1.25 guchar *src = (guchar *)SvPVbyte_nolen (src_sv);
905 root 1.2
906     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, w, h);
907     dst = gdk_pixbuf_get_pixels (RETVAL);
908     dstr = gdk_pixbuf_get_rowstride (RETVAL);
909    
910     for (y = 0; y < h; y++)
911     for (d = dst + y * dstr, x = 0; x < w; x++)
912     {
913 root 1.3 *d++ = (((*src >> 5) & 7) * 255 + 4) / 7;
914     *d++ = (((*src >> 2) & 7) * 255 + 4) / 7;
915     *d++ = (((*src >> 0) & 3) * 255 + 2) / 3;
916 root 1.2
917     src++;
918     }
919 root 1.3 }
920     OUTPUT:
921     RETVAL
922    
923 root 1.6 #############################################################################
924 root 1.4
925     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::PostScript
926    
927     void
928 root 1.7 dump_ascii85 (PerlIO *fp, GdkPixbuf *pb)
929 root 1.4 CODE:
930     {
931     int w = gdk_pixbuf_get_width (pb);
932     int h = gdk_pixbuf_get_height (pb);
933     int x, y, i;
934     guchar *dst;
935 root 1.7 int bpp = gdk_pixbuf_get_n_channels (pb);
936 root 1.4 guchar *src = gdk_pixbuf_get_pixels (pb);
937     int sstr = gdk_pixbuf_get_rowstride (pb);
938    
939     a85_init ();
940    
941     for (y = 0; y < h; y++)
942     for (x = 0; x < w; x++)
943 root 1.7 for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
944 root 1.4 a85_push (fp, src [x * bpp + y * sstr + i]);
945    
946     a85_finish (fp);
947 root 1.7 }
948    
949     void
950     dump_binary (PerlIO *fp, GdkPixbuf *pb)
951     CODE:
952     {
953     int w = gdk_pixbuf_get_width (pb);
954     int h = gdk_pixbuf_get_height (pb);
955     int x, y, i;
956     guchar *dst;
957     int bpp = gdk_pixbuf_get_n_channels (pb);
958     guchar *src = gdk_pixbuf_get_pixels (pb);
959     int sstr = gdk_pixbuf_get_rowstride (pb);
960    
961     for (y = 0; y < h; y++)
962     for (x = 0; x < w; x++)
963     for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
964     PerlIO_putc (fp, src [x * bpp + y * sstr + i]);
965 root 1.4 }
966 root 1.8
967     #############################################################################
968    
969     MODULE = Gtk2::CV PACKAGE = Gtk2::CV
970    
971     SV *
972     pb_to_hv84 (GdkPixbuf *pb)
973     CODE:
974     {
975     int w = gdk_pixbuf_get_width (pb);
976     int h = gdk_pixbuf_get_height (pb);
977     int x, y;
978     guchar *dst;
979     int bpp = gdk_pixbuf_get_n_channels (pb);
980     guchar *src = gdk_pixbuf_get_pixels (pb);
981     int sstr = gdk_pixbuf_get_rowstride (pb);
982    
983     RETVAL = newSV (6 * 8 * 12 / 8);
984     SvPOK_only (RETVAL);
985     SvCUR_set (RETVAL, 6 * 8 * 12 / 8);
986    
987 root 1.25 dst = (guchar *)SvPVX (RETVAL);
988 root 1.8
989     /* some primitive error distribution + random dithering */
990    
991     for (y = 0; y < h; y++)
992     {
993     guchar *p = src + y * sstr;
994    
995     for (x = 0; x < w; x += 2)
996     {
997     unsigned int r, g, b, h, s, v, H, V1, V2;
998    
999     if (bpp == 3)
1000     r = *p++, g = *p++, b = *p++;
1001     else if (bpp == 1)
1002     r = g = b = *p++;
1003     else
1004     abort ();
1005    
1006     rgb_to_hsv (r, g, b, &h, &s, &v);
1007    
1008     H = (h * 15 / 255) << 4;
1009     V1 = v;
1010    
1011     if (bpp == 3)
1012     r = *p++, g = *p++, b = *p++;
1013     else if (bpp == 1)
1014     r = g = b = *p++;
1015     else
1016     abort ();
1017    
1018     rgb_to_hsv (r, g, b, &h, &s, &v);
1019    
1020     H |= h * 15 / 255;
1021     V2 = v;
1022    
1023     *dst++ = H;
1024     *dst++ = V1;
1025     *dst++ = V2;
1026     }
1027     }
1028     }
1029     OUTPUT:
1030     RETVAL
1031 root 1.4
1032 root 1.9 SV *
1033     hv84_to_av (unsigned char *hv84)
1034     CODE:
1035     {
1036     int i = 72 / 3;
1037     AV *av = newAV ();
1038    
1039     RETVAL = (SV *)newRV_noinc ((SV *)av);
1040     while (i--)
1041     {
1042     int h = *hv84++;
1043     int v1 = *hv84++;
1044     int v2 = *hv84++;
1045    
1046     av_push (av, newSViv (v1));
1047     av_push (av, newSViv ((h >> 4) * 255 / 15));
1048     av_push (av, newSViv (v2));
1049     av_push (av, newSViv ((h & 15) * 255 / 15));
1050     }
1051     }
1052     OUTPUT:
1053     RETVAL
1054 root 1.2
1055 root 1.20 #############################################################################
1056    
1057     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Plugin::RCluster
1058    
1059     SV *
1060 root 1.36 extract_features (SV *ar)
1061     CODE:
1062     {
1063     int i;
1064     AV *av, *result;
1065    
1066     if (!SvROK (ar) || SvTYPE (SvRV (ar)) != SVt_PVAV)
1067 root 1.39 croak ("Not an array ref as first argument to extract_features");
1068 root 1.36
1069     av = (AV *) SvRV (ar);
1070     result = newAV ();
1071    
1072     for (i = 0; i <= av_len (av); ++i)
1073     {
1074     SV *sv = *av_fetch (av, i, 1);
1075     SV *histsv = newSV (9 * sizeof (float) + 1);
1076    
1077     SvPOK_on (histsv);
1078     SvCUR_set (histsv, 9 * sizeof (float));
1079     float *hist = (float *)SvPVX (histsv);
1080    
1081     struct feature f_h, f_s, f_v;
1082     feature_init (&f_h);
1083     feature_init (&f_s);
1084     feature_init (&f_v);
1085    
1086     {
1087     STRLEN len;
1088     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1089     while (len >= 3)
1090     {
1091     unsigned int r, g, b, h, s, v;
1092     r = *buf++; g = *buf++; b = *buf++;
1093     rgb_to_hsv (r, g, b, &h, &s, &v);
1094    
1095     feature_update_pass_1 (&f_h, h);
1096     feature_update_pass_1 (&f_s, s);
1097     feature_update_pass_1 (&f_v, v);
1098    
1099     len -= 3;
1100     }
1101    
1102     feature_finish_pass_1 (&f_h);
1103     feature_finish_pass_1 (&f_s);
1104     feature_finish_pass_1 (&f_v);
1105     }
1106    
1107     {
1108     STRLEN len;
1109     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1110     while (len >= 3)
1111     {
1112     unsigned int r, g, b, h, s, v;
1113     r = *buf++; g = *buf++; b = *buf++;
1114     rgb_to_hsv (r, g, b, &h, &s, &v);
1115    
1116     feature_update_pass_2 (&f_h, h);
1117     feature_update_pass_2 (&f_s, s);
1118     feature_update_pass_2 (&f_v, v);
1119    
1120     len -= 3;
1121     }
1122    
1123     feature_finish_pass_2 (&f_h);
1124     feature_finish_pass_2 (&f_s);
1125     feature_finish_pass_2 (&f_v);
1126     }
1127    
1128     hist [0] = f_h.v1 * 2.; hist [1] = f_h.v2 * 2.; hist [2] = f_h.v3 * 2.;
1129     hist [3] = f_s.v1 ; hist [4] = f_s.v2 ; hist [5] = f_s.v3 ;
1130 root 1.38 hist [6] = f_v.v1 * .5; hist [7] = f_v.v2 * .5; hist [8] = f_v.v3 * .5;
1131 root 1.36
1132     av_push (result, histsv);
1133     }
1134    
1135     RETVAL = newRV_noinc ((SV *)result);
1136     }
1137     OUTPUT:
1138     RETVAL
1139