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