ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/CV.xs
Revision: 1.61
Committed: Sun Jul 25 11:24:48 2021 UTC (2 years, 9 months ago) by root
Branch: MAIN
Changes since 1.60: +29 -0 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.61 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     // skip a rare first graphic control extension block
475     // we assume there is exactly one block
476     if (data[ofs] == 0x21 && data[ofs + 1] == 0xf9)
477     ofs += 3 + data[ofs + 2] + 1;
478    
479     if (data_len >= ofs + 2 + 1 + 11)
480     if (!memcmp (data + ofs, "\x21\xff\x0bNETSCAPE2.0", sizeof ("\x21\xff\x0bNETSCAPE2.0") - 1))
481     RETVAL = "video/gif";
482     }
483     }
484    
485 root 1.53 else
486 root 1.58 XSRETURN_UNDEF;
487 root 1.53 }
488     OUTPUT:
489     RETVAL
490    
491 root 1.17 GdkPixbuf_noinc *
492 root 1.51 decode_webp (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
493 root 1.50 CODE:
494     {
495     #if WEBP
496     STRLEN data_size;
497     int alpha;
498 root 1.54 WebPData data;
499     WebPDemuxer *demux;
500     WebPIterator iter;
501     WebPDecoderConfig config;
502 root 1.51 int inw, inh;
503    
504 root 1.54 data.bytes = (uint8_t *)SvPVbyte (image_data, data_size);
505     data.size = data_size;
506 root 1.50
507     perlinterp_release ();
508    
509     RETVAL = 0;
510    
511 root 1.54 if (!(demux = WebPDemux (&data)))
512     goto err_demux;
513    
514     if (!WebPDemuxGetFrame (demux, 1, &iter))
515     goto err_iter;
516    
517     if (!WebPInitDecoderConfig (&config))
518     goto err_iter;
519 root 1.50
520     config.options.use_threads = 1;
521    
522 root 1.54 if (WebPGetFeatures (iter.fragment.bytes, iter.fragment.size, &config.input) != VP8_STATUS_OK)
523     goto err_iter;
524 root 1.51
525     inw = config.input.width;
526     inh = config.input.height;
527    
528 root 1.50 if (thumbnail)
529     {
530 root 1.51 if (inw * ih > inh * iw)
531     ih = (iw * inh + inw - 1) / inw;
532     else
533     iw = (ih * inw + inh - 1) / inh;
534    
535 root 1.50 config.options.bypass_filtering = 1;
536     config.options.no_fancy_upsampling = 1;
537    
538     config.options.use_scaling = 1;
539     config.options.scaled_width = iw;
540     config.options.scaled_height = ih;
541     }
542     else
543     {
544 root 1.51 iw = inw;
545     ih = inh;
546 root 1.50 }
547    
548 root 1.54 alpha = !!config.input.has_alpha;
549 root 1.50
550 root 1.54 RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, alpha, 8, iw, ih);
551 root 1.50 if (!RETVAL)
552 root 1.54 goto err_iter;
553 root 1.50
554     config.output.colorspace = alpha ? MODE_RGBA : MODE_RGB;
555     config.output.u.RGBA.rgba = gdk_pixbuf_get_pixels (RETVAL);
556     config.output.u.RGBA.stride = gdk_pixbuf_get_rowstride (RETVAL);
557     config.output.u.RGBA.size = gdk_pixbuf_get_byte_length (RETVAL);
558     config.output.is_external_memory = 1;
559    
560 root 1.54 if (WebPDecode (iter.fragment.bytes, iter.fragment.size, &config) != VP8_STATUS_OK)
561 root 1.50 {
562     g_object_unref (RETVAL);
563     RETVAL = 0;
564 root 1.54 goto err_iter;
565 root 1.50 }
566    
567 root 1.54 err_iter:
568     WebPDemuxReleaseIterator (&iter);
569     err_demux:
570 root 1.55 WebPDemuxDelete (demux);
571 root 1.54
572 root 1.50 perlinterp_acquire ();
573     #else
574     croak ("load_webp: webp not enabled at compile time");
575     #endif
576     }
577     OUTPUT:
578     RETVAL
579    
580     GdkPixbuf_noinc *
581 root 1.58 decode_jxl (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
582     CODE:
583     {
584     #if JXL
585     JxlDecoder *dec = JxlDecoderCreate (0);
586     JxlBasicInfo info;
587     const uint8_t *next_in = (uint8_t *)SvPVbyte_nolen (image_data);
588     size_t avail_in = SvCUR (image_data);
589     const char *error = 0;
590     void *runner = 0;
591     struct bmff_box box;
592    
593     perlinterp_release ();
594    
595     RETVAL = 0;
596    
597     error = "JxlDecoderCreate failed";
598     if (!dec)
599     goto done;
600    
601     runner = JxlThreadParallelRunnerCreate (0, JxlThreadParallelRunnerDefaultNumWorkerThreads ());
602    
603     error = "JxlDecoderSetParallelRunner failed";
604     if (JxlDecoderSetParallelRunner (dec, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS)
605     goto done;
606    
607     error = "JxlDecoderSubscribeEvents failed";
608 root 1.59 if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | JXL_DEC_BASIC_INFO) != JXL_DEC_SUCCESS)
609     goto done;
610    
611     error = "JxlDecoderSetInput failed";
612     if (JxlDecoderSetInput (dec, next_in, avail_in) != JXL_DEC_SUCCESS)
613 root 1.58 goto done;
614    
615     for (;;)
616     {
617 root 1.59 JxlDecoderStatus status = JxlDecoderProcessInput (dec);
618 root 1.58
619     printf ("status %d\n",status);
620    
621     switch (status)
622     {
623     case JXL_DEC_ERROR:
624     error = "JxlDecoderProcessInput failed";
625     goto done;
626    
627     case JXL_DEC_NEED_MORE_INPUT:
628     error = "incomplete file";
629     goto done;
630    
631     case JXL_DEC_SUCCESS:
632     goto done;
633    
634     case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
635     {
636     error = "JxlDecoderGetBasicInfo failed";
637     if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS)
638     goto done;
639    
640     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize);
641     error = "unable to allocate pixbuf";
642     if (!RETVAL)
643     goto done;
644    
645     JxlPixelFormat format = {
646     info.alpha_bits ? 4 : 3,
647     JXL_TYPE_UINT8,
648     JXL_NATIVE_ENDIAN,
649     gdk_pixbuf_get_rowstride (RETVAL)
650     };
651    
652     error = "JxlDecoderSetImageOutBuffer failed";
653     if (JxlDecoderSetImageOutBuffer (
654     dec,
655     &format,
656     gdk_pixbuf_get_pixels (RETVAL),
657     gdk_pixbuf_get_byte_length (RETVAL)
658     ) != JXL_DEC_SUCCESS)
659     goto done;
660     }
661     break;
662    
663     default:
664     error = "unexpected event";
665     goto done;
666     }
667     }
668    
669     done:
670     if (dec)
671     JxlDecoderDestroy (dec);
672    
673     if (runner)
674     JxlThreadParallelRunnerDestroy (runner);
675    
676     perlinterp_acquire ();
677    
678     if (error)
679     {
680     if (RETVAL)
681     g_object_unref (RETVAL);
682    
683     croak ("load_jxl: %s", error);
684     }
685     #else
686     croak ("load_jxl: jpeg-xl not enabled at compile time");
687     #endif
688     }
689     OUTPUT:
690     RETVAL
691    
692     GdkPixbuf_noinc *
693 root 1.53 decode_jpeg (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
694 root 1.17 CODE:
695     {
696     struct jpeg_decompress_struct cinfo;
697     struct jpg_err_mgr jerr;
698     int rs;
699     volatile GdkPixbuf *pb = 0;
700 root 1.53 STRLEN data_len;
701     guchar *data = SvPVbyte (image_data, data_len);
702 root 1.23
703 root 1.17 RETVAL = 0;
704    
705 root 1.46 perlinterp_release ();
706    
707 root 1.17 cinfo.err = jpeg_std_error (&jerr.err);
708    
709     jerr.err.error_exit = cv_error_exit;
710     jerr.err.output_message = cv_error_output;
711    
712     if ((rs = setjmp (jerr.setjmp_buffer)))
713     {
714     jpeg_destroy_decompress (&cinfo);
715    
716     if (pb)
717     g_object_unref ((gpointer)pb);
718    
719 root 1.46 perlinterp_acquire ();
720 root 1.17 XSRETURN_UNDEF;
721     }
722    
723 root 1.57 if (!data_len)
724     longjmp (jerr.setjmp_buffer, 4);
725    
726 root 1.17 jpeg_create_decompress (&cinfo);
727 root 1.53 cv_jpeg_mem_src (&cinfo, data, data_len);
728 root 1.17
729     jpeg_read_header (&cinfo, TRUE);
730    
731     cinfo.dct_method = JDCT_DEFAULT;
732     cinfo.do_fancy_upsampling = FALSE; /* worse quality, but nobody compained so far, and gdk-pixbuf does the same */
733     cinfo.do_block_smoothing = FALSE;
734     cinfo.out_color_space = JCS_RGB;
735     cinfo.quantize_colors = FALSE;
736    
737     cinfo.scale_num = 1;
738     cinfo.scale_denom = 1;
739    
740     jpeg_calc_output_dimensions (&cinfo);
741    
742     if (thumbnail)
743     {
744     cinfo.dct_method = JDCT_FASTEST;
745     cinfo.do_fancy_upsampling = FALSE;
746    
747     while (cinfo.scale_denom < 8
748 root 1.47 && cinfo.output_width >= iw*4
749     && cinfo.output_height >= ih*4)
750 root 1.17 {
751     cinfo.scale_denom <<= 1;
752     jpeg_calc_output_dimensions (&cinfo);
753     }
754     }
755    
756 root 1.48 if (cinfo.output_components != 3)
757     longjmp (jerr.setjmp_buffer, 3);
758    
759     if (cinfo.jpeg_color_space == JCS_YCCK || cinfo.jpeg_color_space == JCS_CMYK)
760     {
761     cinfo.out_color_space = JCS_CMYK;
762     cinfo.output_components = 4;
763     }
764    
765     pb = RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo.output_components == 4, 8, cinfo.output_width, cinfo.output_height);
766 root 1.17 if (!RETVAL)
767     longjmp (jerr.setjmp_buffer, 2);
768    
769     data = gdk_pixbuf_get_pixels (RETVAL);
770     rs = gdk_pixbuf_get_rowstride (RETVAL);
771    
772     jpeg_start_decompress (&cinfo);
773    
774     while (cinfo.output_scanline < cinfo.output_height)
775     {
776     int remaining = cinfo.output_height - cinfo.output_scanline;
777     JSAMPROW rp[4];
778    
779     rp [0] = data + cinfo.output_scanline * rs;
780     rp [1] = (guchar *)rp [0] + rs;
781     rp [2] = (guchar *)rp [1] + rs;
782     rp [3] = (guchar *)rp [2] + rs;
783    
784     jpeg_read_scanlines (&cinfo, rp, remaining < 4 ? remaining : 4);
785     }
786    
787 root 1.48 if (cinfo.out_color_space == JCS_CMYK)
788     {
789     guchar *end = data + cinfo.output_height * rs;
790    
791     while (data < end)
792     {
793     U32 c = data [0];
794     U32 m = data [1];
795     U32 y = data [2];
796     U32 k = data [3];
797    
798 root 1.49 if (0)
799     if (cinfo.Adobe_transform == 2)
800     {
801     c ^= 0xff;
802     m ^= 0xff;
803     y ^= 0xff;
804     k ^= 0xff;
805     }
806    
807     data [0] = (c * k + 0x80) / 0xff;
808     data [1] = (m * k + 0x80) / 0xff;
809     data [2] = (y * k + 0x80) / 0xff;
810     data [3] = 0xff;
811 root 1.48
812     data += 4;
813     }
814     }
815    
816 root 1.17 jpeg_finish_decompress (&cinfo);
817     jpeg_destroy_decompress (&cinfo);
818 root 1.46 perlinterp_acquire ();
819 root 1.17 }
820     OUTPUT:
821     RETVAL
822    
823 root 1.33 void
824     compare (GdkPixbuf *a, GdkPixbuf *b)
825     PPCODE:
826 root 1.46 perlinterp_release ();
827 root 1.33 {
828     int w = gdk_pixbuf_get_width (a);
829     int h = gdk_pixbuf_get_height (a);
830 root 1.43
831 root 1.33 int sa = gdk_pixbuf_get_rowstride (a);
832     int sb = gdk_pixbuf_get_rowstride (b);
833    
834     guchar *pa = gdk_pixbuf_get_pixels (a);
835     guchar *pb = gdk_pixbuf_get_pixels (b);
836    
837     int x, y;
838    
839     assert (w == gdk_pixbuf_get_width (b));
840     assert (h == gdk_pixbuf_get_height (b));
841    
842     assert (gdk_pixbuf_get_n_channels (a) == 3);
843     assert (gdk_pixbuf_get_n_channels (b) == 3);
844    
845     double diff = 0.;
846     int peak = 0;
847    
848 root 1.35 if (w && h)
849 root 1.34 for (y = 0; y < h; y++)
850     {
851     guchar *pa_ = pa + y * sa;
852     guchar *pb_ = pb + y * sb;
853 root 1.33
854 root 1.34 for (x = 0; x < w; x++)
855     {
856     int d;
857    
858     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
859     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
860     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
861     }
862     }
863 root 1.33
864 root 1.46 perlinterp_acquire ();
865    
866 root 1.33 EXTEND (SP, 2);
867     PUSHs (sv_2mortal (newSVnv (sqrt (diff / (w * h * 3. * 255. * 255.)))));
868     PUSHs (sv_2mortal (newSVnv (peak / 255.)));
869     }
870    
871 root 1.6 #############################################################################
872    
873 root 1.2 MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Schnauzer
874    
875 root 1.41 # currently only works for filenames (octet strings)
876    
877 root 1.18 SV *
878     foldcase (SV *pathsv)
879     PROTOTYPE: $
880     CODE:
881     {
882     STRLEN plen;
883 root 1.41 U8 *path = (U8 *)SvPV (pathsv, plen);
884 root 1.18 U8 *pend = path + plen;
885 root 1.60 U8 dst [plen * 8 * 3], *dstp = dst;
886 root 1.18
887     while (path < pend)
888     {
889 root 1.19 U8 ch = *path;
890    
891     if (ch >= 'a' && ch <= 'z')
892     *dstp++ = *path++;
893 root 1.41 else if (ch >= 'A' && ch <= 'Z')
894     *dstp++ = *path++ + ('a' - 'A');
895 root 1.19 else if (ch >= '0' && ch <= '9')
896 root 1.18 {
897 root 1.60 /* version sort, up to 8 digits */
898 root 1.18 STRLEN el, nl = 0;
899 root 1.19 while (*path >= '0' && *path <= '9' && path < pend)
900 root 1.18 path++, nl++;
901    
902 root 1.60 for (el = nl; el < 8; el++)
903 root 1.18 *dstp++ = '0';
904    
905     memcpy (dstp, path - nl, nl);
906     dstp += nl;
907     }
908     else
909 root 1.41 *dstp++ = *path++;
910     #if 0
911     else
912 root 1.18 {
913     STRLEN cl;
914     to_utf8_fold (path, dstp, &cl);
915     dstp += cl;
916     path += is_utf8_char (path);
917     }
918 root 1.41 #endif
919 root 1.18 }
920    
921 root 1.25 RETVAL = newSVpvn ((const char *)dst, dstp - dst);
922 root 1.18 }
923     OUTPUT:
924     RETVAL
925    
926 root 1.3 GdkPixbuf_noinc *
927 root 1.24 p7_to_pb (int w, int h, SV *src_sv)
928 root 1.21 PROTOTYPE: @
929 root 1.2 CODE:
930     {
931     int x, y;
932     guchar *dst, *d;
933     int dstr;
934 root 1.25 guchar *src = (guchar *)SvPVbyte_nolen (src_sv);
935 root 1.2
936     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, w, h);
937     dst = gdk_pixbuf_get_pixels (RETVAL);
938     dstr = gdk_pixbuf_get_rowstride (RETVAL);
939    
940     for (y = 0; y < h; y++)
941     for (d = dst + y * dstr, x = 0; x < w; x++)
942     {
943 root 1.3 *d++ = (((*src >> 5) & 7) * 255 + 4) / 7;
944     *d++ = (((*src >> 2) & 7) * 255 + 4) / 7;
945     *d++ = (((*src >> 0) & 3) * 255 + 2) / 3;
946 root 1.2
947     src++;
948     }
949 root 1.3 }
950     OUTPUT:
951     RETVAL
952    
953 root 1.6 #############################################################################
954 root 1.4
955     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::PostScript
956    
957     void
958 root 1.7 dump_ascii85 (PerlIO *fp, GdkPixbuf *pb)
959 root 1.4 CODE:
960     {
961     int w = gdk_pixbuf_get_width (pb);
962     int h = gdk_pixbuf_get_height (pb);
963     int x, y, i;
964     guchar *dst;
965 root 1.7 int bpp = gdk_pixbuf_get_n_channels (pb);
966 root 1.4 guchar *src = gdk_pixbuf_get_pixels (pb);
967     int sstr = gdk_pixbuf_get_rowstride (pb);
968    
969     a85_init ();
970    
971     for (y = 0; y < h; y++)
972     for (x = 0; x < w; x++)
973 root 1.7 for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
974 root 1.4 a85_push (fp, src [x * bpp + y * sstr + i]);
975    
976     a85_finish (fp);
977 root 1.7 }
978    
979     void
980     dump_binary (PerlIO *fp, GdkPixbuf *pb)
981     CODE:
982     {
983     int w = gdk_pixbuf_get_width (pb);
984     int h = gdk_pixbuf_get_height (pb);
985     int x, y, i;
986     guchar *dst;
987     int bpp = gdk_pixbuf_get_n_channels (pb);
988     guchar *src = gdk_pixbuf_get_pixels (pb);
989     int sstr = gdk_pixbuf_get_rowstride (pb);
990    
991     for (y = 0; y < h; y++)
992     for (x = 0; x < w; x++)
993     for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
994     PerlIO_putc (fp, src [x * bpp + y * sstr + i]);
995 root 1.4 }
996 root 1.8
997     #############################################################################
998    
999     MODULE = Gtk2::CV PACKAGE = Gtk2::CV
1000    
1001     SV *
1002     pb_to_hv84 (GdkPixbuf *pb)
1003     CODE:
1004     {
1005     int w = gdk_pixbuf_get_width (pb);
1006     int h = gdk_pixbuf_get_height (pb);
1007     int x, y;
1008     guchar *dst;
1009     int bpp = gdk_pixbuf_get_n_channels (pb);
1010     guchar *src = gdk_pixbuf_get_pixels (pb);
1011     int sstr = gdk_pixbuf_get_rowstride (pb);
1012    
1013     RETVAL = newSV (6 * 8 * 12 / 8);
1014     SvPOK_only (RETVAL);
1015     SvCUR_set (RETVAL, 6 * 8 * 12 / 8);
1016    
1017 root 1.25 dst = (guchar *)SvPVX (RETVAL);
1018 root 1.8
1019     /* some primitive error distribution + random dithering */
1020    
1021     for (y = 0; y < h; y++)
1022     {
1023     guchar *p = src + y * sstr;
1024    
1025     for (x = 0; x < w; x += 2)
1026     {
1027     unsigned int r, g, b, h, s, v, H, V1, V2;
1028    
1029     if (bpp == 3)
1030     r = *p++, g = *p++, b = *p++;
1031     else if (bpp == 1)
1032     r = g = b = *p++;
1033     else
1034     abort ();
1035    
1036     rgb_to_hsv (r, g, b, &h, &s, &v);
1037    
1038     H = (h * 15 / 255) << 4;
1039     V1 = v;
1040    
1041     if (bpp == 3)
1042     r = *p++, g = *p++, b = *p++;
1043     else if (bpp == 1)
1044     r = g = b = *p++;
1045     else
1046     abort ();
1047    
1048     rgb_to_hsv (r, g, b, &h, &s, &v);
1049    
1050     H |= h * 15 / 255;
1051     V2 = v;
1052    
1053     *dst++ = H;
1054     *dst++ = V1;
1055     *dst++ = V2;
1056     }
1057     }
1058     }
1059     OUTPUT:
1060     RETVAL
1061 root 1.4
1062 root 1.9 SV *
1063     hv84_to_av (unsigned char *hv84)
1064     CODE:
1065     {
1066     int i = 72 / 3;
1067     AV *av = newAV ();
1068    
1069     RETVAL = (SV *)newRV_noinc ((SV *)av);
1070     while (i--)
1071     {
1072     int h = *hv84++;
1073     int v1 = *hv84++;
1074     int v2 = *hv84++;
1075    
1076     av_push (av, newSViv (v1));
1077     av_push (av, newSViv ((h >> 4) * 255 / 15));
1078     av_push (av, newSViv (v2));
1079     av_push (av, newSViv ((h & 15) * 255 / 15));
1080     }
1081     }
1082     OUTPUT:
1083     RETVAL
1084 root 1.2
1085 root 1.20 #############################################################################
1086    
1087     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Plugin::RCluster
1088    
1089     SV *
1090 root 1.36 extract_features (SV *ar)
1091     CODE:
1092     {
1093     int i;
1094     AV *av, *result;
1095    
1096     if (!SvROK (ar) || SvTYPE (SvRV (ar)) != SVt_PVAV)
1097 root 1.39 croak ("Not an array ref as first argument to extract_features");
1098 root 1.36
1099     av = (AV *) SvRV (ar);
1100     result = newAV ();
1101    
1102     for (i = 0; i <= av_len (av); ++i)
1103     {
1104     SV *sv = *av_fetch (av, i, 1);
1105     SV *histsv = newSV (9 * sizeof (float) + 1);
1106    
1107     SvPOK_on (histsv);
1108     SvCUR_set (histsv, 9 * sizeof (float));
1109     float *hist = (float *)SvPVX (histsv);
1110    
1111     struct feature f_h, f_s, f_v;
1112     feature_init (&f_h);
1113     feature_init (&f_s);
1114     feature_init (&f_v);
1115    
1116     {
1117     STRLEN len;
1118     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1119     while (len >= 3)
1120     {
1121     unsigned int r, g, b, h, s, v;
1122     r = *buf++; g = *buf++; b = *buf++;
1123     rgb_to_hsv (r, g, b, &h, &s, &v);
1124    
1125     feature_update_pass_1 (&f_h, h);
1126     feature_update_pass_1 (&f_s, s);
1127     feature_update_pass_1 (&f_v, v);
1128    
1129     len -= 3;
1130     }
1131    
1132     feature_finish_pass_1 (&f_h);
1133     feature_finish_pass_1 (&f_s);
1134     feature_finish_pass_1 (&f_v);
1135     }
1136    
1137     {
1138     STRLEN len;
1139     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1140     while (len >= 3)
1141     {
1142     unsigned int r, g, b, h, s, v;
1143     r = *buf++; g = *buf++; b = *buf++;
1144     rgb_to_hsv (r, g, b, &h, &s, &v);
1145    
1146     feature_update_pass_2 (&f_h, h);
1147     feature_update_pass_2 (&f_s, s);
1148     feature_update_pass_2 (&f_v, v);
1149    
1150     len -= 3;
1151     }
1152    
1153     feature_finish_pass_2 (&f_h);
1154     feature_finish_pass_2 (&f_s);
1155     feature_finish_pass_2 (&f_v);
1156     }
1157    
1158     hist [0] = f_h.v1 * 2.; hist [1] = f_h.v2 * 2.; hist [2] = f_h.v3 * 2.;
1159     hist [3] = f_s.v1 ; hist [4] = f_s.v2 ; hist [5] = f_s.v3 ;
1160 root 1.38 hist [6] = f_v.v1 * .5; hist [7] = f_v.v2 * .5; hist [8] = f_v.v3 * .5;
1161 root 1.36
1162     av_push (result, histsv);
1163     }
1164    
1165     RETVAL = newRV_noinc ((SV *)result);
1166     }
1167     OUTPUT:
1168     RETVAL
1169