ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/CV.xs
Revision: 1.60
Committed: Thu Jul 15 00:41:44 2021 UTC (2 years, 10 months ago) by root
Branch: MAIN
Changes since 1.59: +3 -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 root 1.60 U8 dst [plen * 8 * 3], *dstp = dst;
857 root 1.18
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 root 1.60 /* version sort, up to 8 digits */
869 root 1.18 STRLEN el, nl = 0;
870 root 1.19 while (*path >= '0' && *path <= '9' && path < pend)
871 root 1.18 path++, nl++;
872    
873 root 1.60 for (el = nl; el < 8; el++)
874 root 1.18 *dstp++ = '0';
875    
876     memcpy (dstp, path - nl, nl);
877     dstp += nl;
878     }
879     else
880 root 1.41 *dstp++ = *path++;
881     #if 0
882     else
883 root 1.18 {
884     STRLEN cl;
885     to_utf8_fold (path, dstp, &cl);
886     dstp += cl;
887     path += is_utf8_char (path);
888     }
889 root 1.41 #endif
890 root 1.18 }
891    
892 root 1.25 RETVAL = newSVpvn ((const char *)dst, dstp - dst);
893 root 1.18 }
894     OUTPUT:
895     RETVAL
896    
897 root 1.3 GdkPixbuf_noinc *
898 root 1.24 p7_to_pb (int w, int h, SV *src_sv)
899 root 1.21 PROTOTYPE: @
900 root 1.2 CODE:
901     {
902     int x, y;
903     guchar *dst, *d;
904     int dstr;
905 root 1.25 guchar *src = (guchar *)SvPVbyte_nolen (src_sv);
906 root 1.2
907     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, w, h);
908     dst = gdk_pixbuf_get_pixels (RETVAL);
909     dstr = gdk_pixbuf_get_rowstride (RETVAL);
910    
911     for (y = 0; y < h; y++)
912     for (d = dst + y * dstr, x = 0; x < w; x++)
913     {
914 root 1.3 *d++ = (((*src >> 5) & 7) * 255 + 4) / 7;
915     *d++ = (((*src >> 2) & 7) * 255 + 4) / 7;
916     *d++ = (((*src >> 0) & 3) * 255 + 2) / 3;
917 root 1.2
918     src++;
919     }
920 root 1.3 }
921     OUTPUT:
922     RETVAL
923    
924 root 1.6 #############################################################################
925 root 1.4
926     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::PostScript
927    
928     void
929 root 1.7 dump_ascii85 (PerlIO *fp, GdkPixbuf *pb)
930 root 1.4 CODE:
931     {
932     int w = gdk_pixbuf_get_width (pb);
933     int h = gdk_pixbuf_get_height (pb);
934     int x, y, i;
935     guchar *dst;
936 root 1.7 int bpp = gdk_pixbuf_get_n_channels (pb);
937 root 1.4 guchar *src = gdk_pixbuf_get_pixels (pb);
938     int sstr = gdk_pixbuf_get_rowstride (pb);
939    
940     a85_init ();
941    
942     for (y = 0; y < h; y++)
943     for (x = 0; x < w; x++)
944 root 1.7 for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
945 root 1.4 a85_push (fp, src [x * bpp + y * sstr + i]);
946    
947     a85_finish (fp);
948 root 1.7 }
949    
950     void
951     dump_binary (PerlIO *fp, GdkPixbuf *pb)
952     CODE:
953     {
954     int w = gdk_pixbuf_get_width (pb);
955     int h = gdk_pixbuf_get_height (pb);
956     int x, y, i;
957     guchar *dst;
958     int bpp = gdk_pixbuf_get_n_channels (pb);
959     guchar *src = gdk_pixbuf_get_pixels (pb);
960     int sstr = gdk_pixbuf_get_rowstride (pb);
961    
962     for (y = 0; y < h; y++)
963     for (x = 0; x < w; x++)
964     for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
965     PerlIO_putc (fp, src [x * bpp + y * sstr + i]);
966 root 1.4 }
967 root 1.8
968     #############################################################################
969    
970     MODULE = Gtk2::CV PACKAGE = Gtk2::CV
971    
972     SV *
973     pb_to_hv84 (GdkPixbuf *pb)
974     CODE:
975     {
976     int w = gdk_pixbuf_get_width (pb);
977     int h = gdk_pixbuf_get_height (pb);
978     int x, y;
979     guchar *dst;
980     int bpp = gdk_pixbuf_get_n_channels (pb);
981     guchar *src = gdk_pixbuf_get_pixels (pb);
982     int sstr = gdk_pixbuf_get_rowstride (pb);
983    
984     RETVAL = newSV (6 * 8 * 12 / 8);
985     SvPOK_only (RETVAL);
986     SvCUR_set (RETVAL, 6 * 8 * 12 / 8);
987    
988 root 1.25 dst = (guchar *)SvPVX (RETVAL);
989 root 1.8
990     /* some primitive error distribution + random dithering */
991    
992     for (y = 0; y < h; y++)
993     {
994     guchar *p = src + y * sstr;
995    
996     for (x = 0; x < w; x += 2)
997     {
998     unsigned int r, g, b, h, s, v, H, V1, V2;
999    
1000     if (bpp == 3)
1001     r = *p++, g = *p++, b = *p++;
1002     else if (bpp == 1)
1003     r = g = b = *p++;
1004     else
1005     abort ();
1006    
1007     rgb_to_hsv (r, g, b, &h, &s, &v);
1008    
1009     H = (h * 15 / 255) << 4;
1010     V1 = v;
1011    
1012     if (bpp == 3)
1013     r = *p++, g = *p++, b = *p++;
1014     else if (bpp == 1)
1015     r = g = b = *p++;
1016     else
1017     abort ();
1018    
1019     rgb_to_hsv (r, g, b, &h, &s, &v);
1020    
1021     H |= h * 15 / 255;
1022     V2 = v;
1023    
1024     *dst++ = H;
1025     *dst++ = V1;
1026     *dst++ = V2;
1027     }
1028     }
1029     }
1030     OUTPUT:
1031     RETVAL
1032 root 1.4
1033 root 1.9 SV *
1034     hv84_to_av (unsigned char *hv84)
1035     CODE:
1036     {
1037     int i = 72 / 3;
1038     AV *av = newAV ();
1039    
1040     RETVAL = (SV *)newRV_noinc ((SV *)av);
1041     while (i--)
1042     {
1043     int h = *hv84++;
1044     int v1 = *hv84++;
1045     int v2 = *hv84++;
1046    
1047     av_push (av, newSViv (v1));
1048     av_push (av, newSViv ((h >> 4) * 255 / 15));
1049     av_push (av, newSViv (v2));
1050     av_push (av, newSViv ((h & 15) * 255 / 15));
1051     }
1052     }
1053     OUTPUT:
1054     RETVAL
1055 root 1.2
1056 root 1.20 #############################################################################
1057    
1058     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Plugin::RCluster
1059    
1060     SV *
1061 root 1.36 extract_features (SV *ar)
1062     CODE:
1063     {
1064     int i;
1065     AV *av, *result;
1066    
1067     if (!SvROK (ar) || SvTYPE (SvRV (ar)) != SVt_PVAV)
1068 root 1.39 croak ("Not an array ref as first argument to extract_features");
1069 root 1.36
1070     av = (AV *) SvRV (ar);
1071     result = newAV ();
1072    
1073     for (i = 0; i <= av_len (av); ++i)
1074     {
1075     SV *sv = *av_fetch (av, i, 1);
1076     SV *histsv = newSV (9 * sizeof (float) + 1);
1077    
1078     SvPOK_on (histsv);
1079     SvCUR_set (histsv, 9 * sizeof (float));
1080     float *hist = (float *)SvPVX (histsv);
1081    
1082     struct feature f_h, f_s, f_v;
1083     feature_init (&f_h);
1084     feature_init (&f_s);
1085     feature_init (&f_v);
1086    
1087     {
1088     STRLEN len;
1089     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1090     while (len >= 3)
1091     {
1092     unsigned int r, g, b, h, s, v;
1093     r = *buf++; g = *buf++; b = *buf++;
1094     rgb_to_hsv (r, g, b, &h, &s, &v);
1095    
1096     feature_update_pass_1 (&f_h, h);
1097     feature_update_pass_1 (&f_s, s);
1098     feature_update_pass_1 (&f_v, v);
1099    
1100     len -= 3;
1101     }
1102    
1103     feature_finish_pass_1 (&f_h);
1104     feature_finish_pass_1 (&f_s);
1105     feature_finish_pass_1 (&f_v);
1106     }
1107    
1108     {
1109     STRLEN len;
1110     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1111     while (len >= 3)
1112     {
1113     unsigned int r, g, b, h, s, v;
1114     r = *buf++; g = *buf++; b = *buf++;
1115     rgb_to_hsv (r, g, b, &h, &s, &v);
1116    
1117     feature_update_pass_2 (&f_h, h);
1118     feature_update_pass_2 (&f_s, s);
1119     feature_update_pass_2 (&f_v, v);
1120    
1121     len -= 3;
1122     }
1123    
1124     feature_finish_pass_2 (&f_h);
1125     feature_finish_pass_2 (&f_s);
1126     feature_finish_pass_2 (&f_v);
1127     }
1128    
1129     hist [0] = f_h.v1 * 2.; hist [1] = f_h.v2 * 2.; hist [2] = f_h.v3 * 2.;
1130     hist [3] = f_s.v1 ; hist [4] = f_s.v2 ; hist [5] = f_s.v3 ;
1131 root 1.38 hist [6] = f_v.v1 * .5; hist [7] = f_v.v2 * .5; hist [8] = f_v.v3 * .5;
1132 root 1.36
1133     av_push (result, histsv);
1134     }
1135    
1136     RETVAL = newRV_noinc ((SV *)result);
1137     }
1138     OUTPUT:
1139     RETVAL
1140