ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/CV.xs
Revision: 1.58
Committed: Mon Jan 4 04:49:04 2021 UTC (3 years, 4 months ago) by root
Branch: MAIN
Changes since 1.57: +164 -6 lines
Log Message:
preliminary jpeg-xl support

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     if (JxlDecoderSubscribeEvents (dec, JXL_DEC_FULL_IMAGE | 0xffc0) != JXL_DEC_SUCCESS)
580     goto done;
581    
582     for (;;)
583     {
584     JxlDecoderStatus status = JxlDecoderProcessInput (dec, &next_in, &avail_in);
585    
586     printf ("status %d\n",status);
587    
588     switch (status)
589     {
590     case JXL_DEC_ERROR:
591     error = "JxlDecoderProcessInput failed";
592     goto done;
593    
594     case JXL_DEC_NEED_MORE_INPUT:
595     error = "incomplete file";
596     goto done;
597    
598     case JXL_DEC_SUCCESS:
599     goto done;
600    
601     case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
602     {
603     error = "JxlDecoderGetBasicInfo failed";
604     if (JxlDecoderGetBasicInfo (dec, &info) != JXL_DEC_SUCCESS)
605     goto done;
606    
607     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, !!info.alpha_bits, 8, info.xsize, info.ysize);
608     error = "unable to allocate pixbuf";
609     if (!RETVAL)
610     goto done;
611    
612     JxlPixelFormat format = {
613     info.alpha_bits ? 4 : 3,
614     JXL_TYPE_UINT8,
615     JXL_NATIVE_ENDIAN,
616     gdk_pixbuf_get_rowstride (RETVAL)
617     };
618    
619     error = "JxlDecoderSetImageOutBuffer failed";
620     if (JxlDecoderSetImageOutBuffer (
621     dec,
622     &format,
623     gdk_pixbuf_get_pixels (RETVAL),
624     gdk_pixbuf_get_byte_length (RETVAL)
625     ) != JXL_DEC_SUCCESS)
626     goto done;
627     }
628     break;
629    
630     default:
631     error = "unexpected event";
632     goto done;
633     }
634     }
635    
636     done:
637     if (dec)
638     JxlDecoderDestroy (dec);
639    
640     if (runner)
641     JxlThreadParallelRunnerDestroy (runner);
642    
643     perlinterp_acquire ();
644    
645     if (error)
646     {
647     if (RETVAL)
648     g_object_unref (RETVAL);
649    
650     croak ("load_jxl: %s", error);
651     }
652     #else
653     croak ("load_jxl: jpeg-xl not enabled at compile time");
654     #endif
655     }
656     OUTPUT:
657     RETVAL
658    
659     GdkPixbuf_noinc *
660 root 1.53 decode_jpeg (SV *image_data, int thumbnail = 0, int iw = 0, int ih = 0)
661 root 1.17 CODE:
662     {
663     struct jpeg_decompress_struct cinfo;
664     struct jpg_err_mgr jerr;
665     int rs;
666     volatile GdkPixbuf *pb = 0;
667 root 1.53 STRLEN data_len;
668     guchar *data = SvPVbyte (image_data, data_len);
669 root 1.23
670 root 1.17 RETVAL = 0;
671    
672 root 1.46 perlinterp_release ();
673    
674 root 1.17 cinfo.err = jpeg_std_error (&jerr.err);
675    
676     jerr.err.error_exit = cv_error_exit;
677     jerr.err.output_message = cv_error_output;
678    
679     if ((rs = setjmp (jerr.setjmp_buffer)))
680     {
681     jpeg_destroy_decompress (&cinfo);
682    
683     if (pb)
684     g_object_unref ((gpointer)pb);
685    
686 root 1.46 perlinterp_acquire ();
687 root 1.17 XSRETURN_UNDEF;
688     }
689    
690 root 1.57 if (!data_len)
691     longjmp (jerr.setjmp_buffer, 4);
692    
693 root 1.17 jpeg_create_decompress (&cinfo);
694 root 1.53 cv_jpeg_mem_src (&cinfo, data, data_len);
695 root 1.17
696     jpeg_read_header (&cinfo, TRUE);
697    
698     cinfo.dct_method = JDCT_DEFAULT;
699     cinfo.do_fancy_upsampling = FALSE; /* worse quality, but nobody compained so far, and gdk-pixbuf does the same */
700     cinfo.do_block_smoothing = FALSE;
701     cinfo.out_color_space = JCS_RGB;
702     cinfo.quantize_colors = FALSE;
703    
704     cinfo.scale_num = 1;
705     cinfo.scale_denom = 1;
706    
707     jpeg_calc_output_dimensions (&cinfo);
708    
709     if (thumbnail)
710     {
711     cinfo.dct_method = JDCT_FASTEST;
712     cinfo.do_fancy_upsampling = FALSE;
713    
714     while (cinfo.scale_denom < 8
715 root 1.47 && cinfo.output_width >= iw*4
716     && cinfo.output_height >= ih*4)
717 root 1.17 {
718     cinfo.scale_denom <<= 1;
719     jpeg_calc_output_dimensions (&cinfo);
720     }
721     }
722    
723 root 1.48 if (cinfo.output_components != 3)
724     longjmp (jerr.setjmp_buffer, 3);
725    
726     if (cinfo.jpeg_color_space == JCS_YCCK || cinfo.jpeg_color_space == JCS_CMYK)
727     {
728     cinfo.out_color_space = JCS_CMYK;
729     cinfo.output_components = 4;
730     }
731    
732     pb = RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo.output_components == 4, 8, cinfo.output_width, cinfo.output_height);
733 root 1.17 if (!RETVAL)
734     longjmp (jerr.setjmp_buffer, 2);
735    
736     data = gdk_pixbuf_get_pixels (RETVAL);
737     rs = gdk_pixbuf_get_rowstride (RETVAL);
738    
739     jpeg_start_decompress (&cinfo);
740    
741     while (cinfo.output_scanline < cinfo.output_height)
742     {
743     int remaining = cinfo.output_height - cinfo.output_scanline;
744     JSAMPROW rp[4];
745    
746     rp [0] = data + cinfo.output_scanline * rs;
747     rp [1] = (guchar *)rp [0] + rs;
748     rp [2] = (guchar *)rp [1] + rs;
749     rp [3] = (guchar *)rp [2] + rs;
750    
751     jpeg_read_scanlines (&cinfo, rp, remaining < 4 ? remaining : 4);
752     }
753    
754 root 1.48 if (cinfo.out_color_space == JCS_CMYK)
755     {
756     guchar *end = data + cinfo.output_height * rs;
757    
758     while (data < end)
759     {
760     U32 c = data [0];
761     U32 m = data [1];
762     U32 y = data [2];
763     U32 k = data [3];
764    
765 root 1.49 if (0)
766     if (cinfo.Adobe_transform == 2)
767     {
768     c ^= 0xff;
769     m ^= 0xff;
770     y ^= 0xff;
771     k ^= 0xff;
772     }
773    
774     data [0] = (c * k + 0x80) / 0xff;
775     data [1] = (m * k + 0x80) / 0xff;
776     data [2] = (y * k + 0x80) / 0xff;
777     data [3] = 0xff;
778 root 1.48
779     data += 4;
780     }
781     }
782    
783 root 1.17 jpeg_finish_decompress (&cinfo);
784     jpeg_destroy_decompress (&cinfo);
785 root 1.46 perlinterp_acquire ();
786 root 1.17 }
787     OUTPUT:
788     RETVAL
789    
790 root 1.33 void
791     compare (GdkPixbuf *a, GdkPixbuf *b)
792     PPCODE:
793 root 1.46 perlinterp_release ();
794 root 1.33 {
795     int w = gdk_pixbuf_get_width (a);
796     int h = gdk_pixbuf_get_height (a);
797 root 1.43
798 root 1.33 int sa = gdk_pixbuf_get_rowstride (a);
799     int sb = gdk_pixbuf_get_rowstride (b);
800    
801     guchar *pa = gdk_pixbuf_get_pixels (a);
802     guchar *pb = gdk_pixbuf_get_pixels (b);
803    
804     int x, y;
805    
806     assert (w == gdk_pixbuf_get_width (b));
807     assert (h == gdk_pixbuf_get_height (b));
808    
809     assert (gdk_pixbuf_get_n_channels (a) == 3);
810     assert (gdk_pixbuf_get_n_channels (b) == 3);
811    
812     double diff = 0.;
813     int peak = 0;
814    
815 root 1.35 if (w && h)
816 root 1.34 for (y = 0; y < h; y++)
817     {
818     guchar *pa_ = pa + y * sa;
819     guchar *pb_ = pb + y * sb;
820 root 1.33
821 root 1.34 for (x = 0; x < w; x++)
822     {
823     int d;
824    
825     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
826     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
827     d = ((int)*pa_++) - ((int)*pb_++); diff += d*d; peak = MAX (peak, abs (d));
828     }
829     }
830 root 1.33
831 root 1.46 perlinterp_acquire ();
832    
833 root 1.33 EXTEND (SP, 2);
834     PUSHs (sv_2mortal (newSVnv (sqrt (diff / (w * h * 3. * 255. * 255.)))));
835     PUSHs (sv_2mortal (newSVnv (peak / 255.)));
836     }
837    
838 root 1.6 #############################################################################
839    
840 root 1.2 MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Schnauzer
841    
842 root 1.41 # currently only works for filenames (octet strings)
843    
844 root 1.18 SV *
845     foldcase (SV *pathsv)
846     PROTOTYPE: $
847     CODE:
848     {
849     STRLEN plen;
850 root 1.41 U8 *path = (U8 *)SvPV (pathsv, plen);
851 root 1.18 U8 *pend = path + plen;
852     U8 dst [plen * 6 * 3], *dstp = dst;
853    
854     while (path < pend)
855     {
856 root 1.19 U8 ch = *path;
857    
858     if (ch >= 'a' && ch <= 'z')
859     *dstp++ = *path++;
860 root 1.41 else if (ch >= 'A' && ch <= 'Z')
861     *dstp++ = *path++ + ('a' - 'A');
862 root 1.19 else if (ch >= '0' && ch <= '9')
863 root 1.18 {
864     STRLEN el, nl = 0;
865 root 1.19 while (*path >= '0' && *path <= '9' && path < pend)
866 root 1.18 path++, nl++;
867    
868     for (el = nl; el < 6; el++)
869     *dstp++ = '0';
870    
871     memcpy (dstp, path - nl, nl);
872     dstp += nl;
873     }
874     else
875 root 1.41 *dstp++ = *path++;
876     #if 0
877     else
878 root 1.18 {
879     STRLEN cl;
880     to_utf8_fold (path, dstp, &cl);
881     dstp += cl;
882     path += is_utf8_char (path);
883     }
884 root 1.41 #endif
885 root 1.18 }
886    
887 root 1.25 RETVAL = newSVpvn ((const char *)dst, dstp - dst);
888 root 1.18 }
889     OUTPUT:
890     RETVAL
891    
892 root 1.3 GdkPixbuf_noinc *
893 root 1.24 p7_to_pb (int w, int h, SV *src_sv)
894 root 1.21 PROTOTYPE: @
895 root 1.2 CODE:
896     {
897     int x, y;
898     guchar *dst, *d;
899     int dstr;
900 root 1.25 guchar *src = (guchar *)SvPVbyte_nolen (src_sv);
901 root 1.2
902     RETVAL = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, w, h);
903     dst = gdk_pixbuf_get_pixels (RETVAL);
904     dstr = gdk_pixbuf_get_rowstride (RETVAL);
905    
906     for (y = 0; y < h; y++)
907     for (d = dst + y * dstr, x = 0; x < w; x++)
908     {
909 root 1.3 *d++ = (((*src >> 5) & 7) * 255 + 4) / 7;
910     *d++ = (((*src >> 2) & 7) * 255 + 4) / 7;
911     *d++ = (((*src >> 0) & 3) * 255 + 2) / 3;
912 root 1.2
913     src++;
914     }
915 root 1.3 }
916     OUTPUT:
917     RETVAL
918    
919 root 1.6 #############################################################################
920 root 1.4
921     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::PostScript
922    
923     void
924 root 1.7 dump_ascii85 (PerlIO *fp, GdkPixbuf *pb)
925 root 1.4 CODE:
926     {
927     int w = gdk_pixbuf_get_width (pb);
928     int h = gdk_pixbuf_get_height (pb);
929     int x, y, i;
930     guchar *dst;
931 root 1.7 int bpp = gdk_pixbuf_get_n_channels (pb);
932 root 1.4 guchar *src = gdk_pixbuf_get_pixels (pb);
933     int sstr = gdk_pixbuf_get_rowstride (pb);
934    
935     a85_init ();
936    
937     for (y = 0; y < h; y++)
938     for (x = 0; x < w; x++)
939 root 1.7 for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
940 root 1.4 a85_push (fp, src [x * bpp + y * sstr + i]);
941    
942     a85_finish (fp);
943 root 1.7 }
944    
945     void
946     dump_binary (PerlIO *fp, GdkPixbuf *pb)
947     CODE:
948     {
949     int w = gdk_pixbuf_get_width (pb);
950     int h = gdk_pixbuf_get_height (pb);
951     int x, y, i;
952     guchar *dst;
953     int bpp = gdk_pixbuf_get_n_channels (pb);
954     guchar *src = gdk_pixbuf_get_pixels (pb);
955     int sstr = gdk_pixbuf_get_rowstride (pb);
956    
957     for (y = 0; y < h; y++)
958     for (x = 0; x < w; x++)
959     for (i = 0; i < (bpp < 3 ? 1 : 3); i++)
960     PerlIO_putc (fp, src [x * bpp + y * sstr + i]);
961 root 1.4 }
962 root 1.8
963     #############################################################################
964    
965     MODULE = Gtk2::CV PACKAGE = Gtk2::CV
966    
967     SV *
968     pb_to_hv84 (GdkPixbuf *pb)
969     CODE:
970     {
971     int w = gdk_pixbuf_get_width (pb);
972     int h = gdk_pixbuf_get_height (pb);
973     int x, y;
974     guchar *dst;
975     int bpp = gdk_pixbuf_get_n_channels (pb);
976     guchar *src = gdk_pixbuf_get_pixels (pb);
977     int sstr = gdk_pixbuf_get_rowstride (pb);
978    
979     RETVAL = newSV (6 * 8 * 12 / 8);
980     SvPOK_only (RETVAL);
981     SvCUR_set (RETVAL, 6 * 8 * 12 / 8);
982    
983 root 1.25 dst = (guchar *)SvPVX (RETVAL);
984 root 1.8
985     /* some primitive error distribution + random dithering */
986    
987     for (y = 0; y < h; y++)
988     {
989     guchar *p = src + y * sstr;
990    
991     for (x = 0; x < w; x += 2)
992     {
993     unsigned int r, g, b, h, s, v, H, V1, V2;
994    
995     if (bpp == 3)
996     r = *p++, g = *p++, b = *p++;
997     else if (bpp == 1)
998     r = g = b = *p++;
999     else
1000     abort ();
1001    
1002     rgb_to_hsv (r, g, b, &h, &s, &v);
1003    
1004     H = (h * 15 / 255) << 4;
1005     V1 = v;
1006    
1007     if (bpp == 3)
1008     r = *p++, g = *p++, b = *p++;
1009     else if (bpp == 1)
1010     r = g = b = *p++;
1011     else
1012     abort ();
1013    
1014     rgb_to_hsv (r, g, b, &h, &s, &v);
1015    
1016     H |= h * 15 / 255;
1017     V2 = v;
1018    
1019     *dst++ = H;
1020     *dst++ = V1;
1021     *dst++ = V2;
1022     }
1023     }
1024     }
1025     OUTPUT:
1026     RETVAL
1027 root 1.4
1028 root 1.9 SV *
1029     hv84_to_av (unsigned char *hv84)
1030     CODE:
1031     {
1032     int i = 72 / 3;
1033     AV *av = newAV ();
1034    
1035     RETVAL = (SV *)newRV_noinc ((SV *)av);
1036     while (i--)
1037     {
1038     int h = *hv84++;
1039     int v1 = *hv84++;
1040     int v2 = *hv84++;
1041    
1042     av_push (av, newSViv (v1));
1043     av_push (av, newSViv ((h >> 4) * 255 / 15));
1044     av_push (av, newSViv (v2));
1045     av_push (av, newSViv ((h & 15) * 255 / 15));
1046     }
1047     }
1048     OUTPUT:
1049     RETVAL
1050 root 1.2
1051 root 1.20 #############################################################################
1052    
1053     MODULE = Gtk2::CV PACKAGE = Gtk2::CV::Plugin::RCluster
1054    
1055     SV *
1056 root 1.36 extract_features (SV *ar)
1057     CODE:
1058     {
1059     int i;
1060     AV *av, *result;
1061    
1062     if (!SvROK (ar) || SvTYPE (SvRV (ar)) != SVt_PVAV)
1063 root 1.39 croak ("Not an array ref as first argument to extract_features");
1064 root 1.36
1065     av = (AV *) SvRV (ar);
1066     result = newAV ();
1067    
1068     for (i = 0; i <= av_len (av); ++i)
1069     {
1070     SV *sv = *av_fetch (av, i, 1);
1071     SV *histsv = newSV (9 * sizeof (float) + 1);
1072    
1073     SvPOK_on (histsv);
1074     SvCUR_set (histsv, 9 * sizeof (float));
1075     float *hist = (float *)SvPVX (histsv);
1076    
1077     struct feature f_h, f_s, f_v;
1078     feature_init (&f_h);
1079     feature_init (&f_s);
1080     feature_init (&f_v);
1081    
1082     {
1083     STRLEN len;
1084     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1085     while (len >= 3)
1086     {
1087     unsigned int r, g, b, h, s, v;
1088     r = *buf++; g = *buf++; b = *buf++;
1089     rgb_to_hsv (r, g, b, &h, &s, &v);
1090    
1091     feature_update_pass_1 (&f_h, h);
1092     feature_update_pass_1 (&f_s, s);
1093     feature_update_pass_1 (&f_v, v);
1094    
1095     len -= 3;
1096     }
1097    
1098     feature_finish_pass_1 (&f_h);
1099     feature_finish_pass_1 (&f_s);
1100     feature_finish_pass_1 (&f_v);
1101     }
1102    
1103     {
1104     STRLEN len;
1105     unsigned char *buf = (unsigned char *)SvPVbyte (sv, len);
1106     while (len >= 3)
1107     {
1108     unsigned int r, g, b, h, s, v;
1109     r = *buf++; g = *buf++; b = *buf++;
1110     rgb_to_hsv (r, g, b, &h, &s, &v);
1111    
1112     feature_update_pass_2 (&f_h, h);
1113     feature_update_pass_2 (&f_s, s);
1114     feature_update_pass_2 (&f_v, v);
1115    
1116     len -= 3;
1117     }
1118    
1119     feature_finish_pass_2 (&f_h);
1120     feature_finish_pass_2 (&f_s);
1121     feature_finish_pass_2 (&f_v);
1122     }
1123    
1124     hist [0] = f_h.v1 * 2.; hist [1] = f_h.v2 * 2.; hist [2] = f_h.v3 * 2.;
1125     hist [3] = f_s.v1 ; hist [4] = f_s.v2 ; hist [5] = f_s.v3 ;
1126 root 1.38 hist [6] = f_v.v1 * .5; hist [7] = f_v.v2 * .5; hist [8] = f_v.v3 * .5;
1127 root 1.36
1128     av_push (result, histsv);
1129     }
1130    
1131     RETVAL = newRV_noinc ((SV *)result);
1132     }
1133     OUTPUT:
1134     RETVAL
1135