morandi 0.12.0 → 0.99.03

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1126 @@
1
+ #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
2
+ /* Includes */
3
+ #include <ruby.h>
4
+ #include <stdlib.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+
8
+ #if defined GCC
9
+ #define OPTIONAL_ATTR __attribute__((unused))
10
+ #else
11
+ #define OPTIONAL_ATTR
12
+ #endif
13
+
14
+ #include "gdk-pixbuf/gdk-pixbuf.h"
15
+ #include "rbglib.h"
16
+ #include "rbgobject.h"
17
+
18
+ /* Prototypes */
19
+ void Init_morandi_native(void);
20
+
21
+ static VALUE mMorandiNative;
22
+ static VALUE cRedEye;
23
+ static VALUE mPixbufUtils;
24
+ static VALUE structRegion;
25
+
26
+
27
+ static VALUE
28
+ RedEye___alloc__(VALUE self OPTIONAL_ATTR);
29
+
30
+ static VALUE
31
+ RedEye_initialize(VALUE self OPTIONAL_ATTR, VALUE __v_pixbuf OPTIONAL_ATTR, VALUE __v_minX OPTIONAL_ATTR, VALUE __v_minY
32
+ OPTIONAL_ATTR, VALUE __v_maxX OPTIONAL_ATTR, VALUE __v_maxY OPTIONAL_ATTR);
33
+
34
+ static VALUE
35
+ RedEye_identify_blobs(int __p_argc, VALUE *__p_argv, VALUE self);
36
+
37
+ static VALUE
38
+ RedEye_correct_blob(VALUE self OPTIONAL_ATTR, VALUE __v_blob_id OPTIONAL_ATTR);
39
+
40
+ static VALUE
41
+ RedEye_highlight_blob(int __p_argc, VALUE *__p_argv, VALUE self);
42
+
43
+ static VALUE
44
+ RedEye_preview_blob(int __p_argc, VALUE *__p_argv, VALUE self);
45
+
46
+ static VALUE
47
+ RedEye_preview(VALUE self OPTIONAL_ATTR);
48
+
49
+ static VALUE
50
+ RedEye_pixbuf(VALUE self OPTIONAL_ATTR);
51
+
52
+ static VALUE
53
+ PixbufUtils_CLASS_contrast(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_adjust OPTIONAL_ATTR);
54
+
55
+ static VALUE
56
+ PixbufUtils_CLASS_brightness(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_adjust OPTIONAL_ATTR);
57
+
58
+ static VALUE
59
+ PixbufUtils_CLASS_filter(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE filter OPTIONAL_ATTR,
60
+ VALUE __v_divisor OPTIONAL_ATTR);
61
+
62
+
63
+ static VALUE
64
+ PixbufUtils_CLASS_rotate(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_angle OPTIONAL_ATTR);
65
+
66
+ static VALUE
67
+ PixbufUtils_CLASS_gamma(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_level OPTIONAL_ATTR);
68
+
69
+ static VALUE
70
+ PixbufUtils_CLASS_tint(int __p_argc, VALUE *__p_argv, VALUE self);
71
+
72
+ static VALUE
73
+ PixbufUtils_CLASS_mask(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_mask OPTIONAL_ATTR);
74
+
75
+ /* Inline C code */
76
+
77
+ #define PIXEL(row, channels, x) ((pixel_t)(row + (channels * x)))
78
+
79
+ typedef struct {
80
+ unsigned char r, g, b, a;
81
+ } *pixel_t;
82
+
83
+ extern void Init_morandi_c(void);
84
+
85
+ #ifndef IGNORE
86
+ #define IGNORE(x) x=x
87
+ #endif
88
+
89
+ #include <unistd.h>
90
+ #include <math.h>
91
+
92
+ #include "rotate.h"
93
+ #include "gamma.h"
94
+ #include "mask.h"
95
+ #include "tint.h"
96
+ #include "filter.h"
97
+
98
+ /*
99
+ GdkPixbuf *pixbuf_op(GdkPixbuf *src, GdkPixbuf *dest,
100
+ */
101
+
102
+ static inline VALUE
103
+ unref_pixbuf(GdkPixbuf *pixbuf) {
104
+ volatile VALUE pb = Qnil;
105
+
106
+ pb = GOBJ2RVAL(pixbuf);
107
+
108
+ g_object_unref(pixbuf);
109
+
110
+ return pb;
111
+ }
112
+
113
+ typedef struct {
114
+ char red, green, blue;
115
+ } rgb_t;
116
+
117
+ typedef struct {
118
+ int minX, maxX, minY, maxY;
119
+ int width, height;
120
+ int noPixels, mergeWith;
121
+ } region_info;
122
+
123
+ typedef struct {
124
+ struct {
125
+ int minX, maxX, minY, maxY;
126
+ int width, height;
127
+ } area;
128
+ struct {
129
+ int *data;
130
+ region_info *region;
131
+ int len, size;
132
+ } regions;
133
+ int *mask;
134
+ GdkPixbuf *pixbuf, *preview;
135
+ } redeyeop_t;
136
+
137
+ #define MIN_RED_VAL 20
138
+
139
+ static void identify_possible_redeye_pixels(redeyeop_t *op,
140
+ double green_sensitivity, double blue_sensitivity,
141
+ int min_red_val) {
142
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
143
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
144
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
145
+
146
+ int y, ry = 0, x, rx = 0;
147
+ for (y = op->area.minY; y < op->area.maxY; y++) {
148
+ guchar *thisLine = data + (rowstride * y);
149
+ guchar *pixel;
150
+
151
+ pixel = thisLine + (op->area.minX * pixWidth);
152
+ rx = 0;
153
+
154
+ for (x = op->area.minX; x < op->area.maxX; x++) {
155
+
156
+ int r, g, b;
157
+
158
+ r = pixel[0];
159
+ g = pixel[1];
160
+ b = pixel[2];
161
+
162
+ gboolean threshMet;
163
+
164
+ threshMet = (((double) r) > (green_sensitivity * (double) g)) &&
165
+ (((double) r) > (blue_sensitivity * (double) b)) &&
166
+ (r > min_red_val);
167
+
168
+ if (threshMet)
169
+ op->mask[rx + ry] = r;
170
+ else
171
+ op->mask[rx + ry] = 0; /* MEMZERO should have done its job ? */
172
+
173
+ pixel += pixWidth;
174
+ rx++;
175
+ }
176
+
177
+ ry += op->area.width;
178
+ }
179
+ }
180
+
181
+ inline int group_at(redeyeop_t *op, int px, int py) {
182
+ int index, region;
183
+
184
+ if (px < 0 || py < 0)
185
+ return 0;
186
+
187
+ index = px + (py * op->area.width);
188
+
189
+ if (index < 0)
190
+ return 0;
191
+ if (index > (op->area.width * op->area.height))
192
+ return 0;
193
+
194
+ region = op->regions.data[index];
195
+ if (region > 0) {
196
+ if (op->regions.region[region].mergeWith) {
197
+ return op->regions.region[region].mergeWith;
198
+ } else {
199
+ return region;
200
+ }
201
+ } else {
202
+ return 0;
203
+ }
204
+ }
205
+
206
+ #define group_for(x, y) group_at(op, x, y)
207
+
208
+ static void identify_blob_groupings(redeyeop_t *op) {
209
+ volatile int next_blob_id = 1, blob_id, y, x;
210
+
211
+ for (y = 0; y < op->area.height; y++) {
212
+ for (x = 0; x < op->area.width; x++) {
213
+ if (op->mask[x + (y * op->area.width)] > 0) {
214
+ gboolean existing = FALSE;
215
+ int sx, sy, group = 0;
216
+ // Target pixel is true
217
+ blob_id = 0;
218
+
219
+ for (sy = y; sy >= y - 1; sy--) {
220
+ sx = (sy == y) ? x : x + 1;
221
+ for (; sx >= (x - 1); sx--) {
222
+ /*if ((sx >= x) && (sy >= y))
223
+ goto blob_scan_done;*/
224
+
225
+ if (sx >= 0 && sy >= 0)
226
+ group = group_for(sx, sy);
227
+
228
+ if (group) {
229
+ existing = TRUE;
230
+ if (blob_id) {
231
+ int target = MIN(blob_id, group);
232
+ int from = MAX(blob_id, group);
233
+
234
+ if (op->regions.region[target].mergeWith > 0) {
235
+ // Already merged
236
+ target = op->regions.region[target].mergeWith;
237
+ }
238
+ op->regions.region[from].mergeWith = target;
239
+
240
+ // Merge blob_id & group
241
+ }
242
+ blob_id = group;
243
+ }
244
+ }
245
+ }
246
+
247
+ if (blob_id == 0) { // Allocate new group
248
+ blob_id = next_blob_id;
249
+ op->regions.region[blob_id].minX = x;
250
+ op->regions.region[blob_id].maxX = x;
251
+ op->regions.region[blob_id].minY = y;
252
+ op->regions.region[blob_id].maxY = y;
253
+ op->regions.region[blob_id].width = 1;
254
+ op->regions.region[blob_id].height = 1;
255
+ op->regions.region[blob_id].noPixels = 1;
256
+ op->regions.region[blob_id].mergeWith = 0;
257
+
258
+ next_blob_id++;
259
+ op->regions.len = next_blob_id;
260
+
261
+ if (next_blob_id >= op->regions.size) {
262
+ int extra, new_size;
263
+
264
+ /*
265
+ * Realloc in increasingly large chunks to reduce memory fragmentation
266
+ */
267
+ extra = op->regions.size;
268
+ new_size = op->regions.size + extra;
269
+
270
+ REALLOC_N(op->regions.region, region_info, new_size);
271
+
272
+ op->regions.size = new_size;
273
+ }
274
+ }
275
+
276
+ if (existing) {
277
+ op->regions.region[blob_id].minX = MIN(x, op->regions.region[blob_id].minX);
278
+ op->regions.region[blob_id].maxX = MAX(x, op->regions.region[blob_id].maxX);
279
+ op->regions.region[blob_id].minY = MIN(y, op->regions.region[blob_id].minY);
280
+ op->regions.region[blob_id].maxY = MAX(y, op->regions.region[blob_id].maxY);
281
+ op->regions.region[blob_id].width = op->regions.region[blob_id].maxX -
282
+ op->regions.region[blob_id].minX + 1;
283
+ op->regions.region[blob_id].height = op->regions.region[blob_id].maxY -
284
+ op->regions.region[blob_id].minY + 1;
285
+ op->regions.region[blob_id].noPixels++;
286
+ }
287
+
288
+ op->regions.data[x + (y * op->area.width)] = blob_id;
289
+ }
290
+ }
291
+ }
292
+ /*FILE *fp = fopen("regions.txt","w");*/
293
+ for (y = 0; y < op->area.height; y++) {
294
+ for (x = 0; x < op->area.width; x++) {
295
+ int g = group_at(op, x, y); // Returns the merged group...
296
+ op->regions.data[x + (y * op->area.width)] = g;
297
+ /*
298
+ if (op->regions.len <= 0xf || 1)
299
+ {
300
+ if (g == 0)
301
+ fprintf(fp, " ");
302
+ else
303
+ fprintf(fp, "%x", g);
304
+ }
305
+ else
306
+ {
307
+ if (g == 0)
308
+ fprintf(fp, " ");
309
+ else
310
+ fprintf(fp, "%x ", g);
311
+ }*/
312
+ }
313
+ /*fprintf(fp, "\n");*/
314
+ }
315
+ /*fclose(fp);*/
316
+ }
317
+
318
+ #define NO_REGIONS_DEFAULT 20
319
+ #define MIN_ID 1
320
+
321
+ static redeyeop_t *new_redeye(void) {
322
+ redeyeop_t *ptr = ALLOC(redeyeop_t);
323
+ MEMZERO(ptr, redeyeop_t, 1);
324
+ return ptr;
325
+ }
326
+
327
+ static void free_redeye(redeyeop_t *ptr) {
328
+ if (ptr->mask)
329
+ free(ptr->mask);
330
+
331
+ if (ptr->regions.data) {
332
+ free(ptr->regions.data);
333
+ }
334
+
335
+ if (ptr->regions.region) {
336
+ free(ptr->regions.region);
337
+ }
338
+
339
+ if (ptr->pixbuf) {
340
+ g_object_unref(ptr->pixbuf);
341
+ }
342
+
343
+ if (ptr->preview) {
344
+ g_object_unref(ptr->preview);
345
+ }
346
+
347
+ free(ptr);
348
+ }
349
+
350
+ static gboolean in_region(redeyeop_t *op, int x, int y, int blob_id) {
351
+ int index;
352
+
353
+ if (x < op->area.minX || x > op->area.maxX ||
354
+ y < op->area.minY || y > op->area.maxY)
355
+ return FALSE;
356
+
357
+ index = (x - op->area.minX) + ((y - op->area.minY) * op->area.width);
358
+
359
+ return op->regions.data[index] == blob_id;
360
+ }
361
+
362
+ static double alpha_level_for_pixel(redeyeop_t *op, int x, int y, int blob_id) {
363
+ int j = 0, c = 0, xm, ym;
364
+
365
+ if (in_region(op, x, y, blob_id))
366
+ return 1.0;
367
+
368
+ for (xm = -2; xm <= 2; xm++) {
369
+ for (ym = -2; ym <= 2; ym++) {
370
+ c++;
371
+ if (xm == 0 && ym == 0)
372
+ continue;
373
+ if (in_region(op, x + xm, y + ym, blob_id))
374
+ j++;
375
+ }
376
+ }
377
+
378
+ return ((double) j) / ((double) c);
379
+ }
380
+
381
+ static unsigned char col(double val) {
382
+ if (val < 0) return 0;
383
+ if (val > 255) return 255;
384
+ return val;
385
+
386
+ }
387
+
388
+ static GdkPixbuf *redeye_preview(redeyeop_t *op, gboolean reset) {
389
+ int width, height;
390
+ width = op->area.width;
391
+ height = op->area.height;
392
+
393
+ if (width + op->area.minX > gdk_pixbuf_get_width(op->pixbuf)) {
394
+ width = gdk_pixbuf_get_width(op->pixbuf) - op->area.minX;
395
+ }
396
+ if (height + op->area.minY > gdk_pixbuf_get_height(op->pixbuf)) {
397
+ height = gdk_pixbuf_get_height(op->pixbuf) - op->area.minY;
398
+ }
399
+
400
+ if (op->preview == NULL) {
401
+ GdkPixbuf *sub = NULL;
402
+ sub = gdk_pixbuf_new_subpixbuf(op->pixbuf, op->area.minX, op->area.minY,
403
+ width, height);
404
+
405
+ op->preview = gdk_pixbuf_copy(sub);
406
+ g_object_unref(sub);
407
+ } else if (reset) {
408
+ gdk_pixbuf_copy_area(op->pixbuf, op->area.minX, op->area.minY,
409
+ width, height, op->preview, 0, 0);
410
+ }
411
+
412
+ return op->preview;
413
+ }
414
+
415
+ static void desaturate_blob(redeyeop_t *op, int blob_id) {
416
+ int y, x;
417
+ int minX, minY, maxX, maxY;
418
+
419
+ minY = MAX(0, op->area.minY + op->regions.region[blob_id].minY - 1);
420
+ maxY = MIN(op->area.maxY + op->regions.region[blob_id].maxY + 1,
421
+ gdk_pixbuf_get_height(op->pixbuf) - 1);
422
+ minX = MAX(0, op->area.minX + op->regions.region[blob_id].minX - 1);
423
+ maxX = MIN(op->area.maxX + op->regions.region[blob_id].maxX + 1,
424
+ gdk_pixbuf_get_width(op->pixbuf) - 1);
425
+
426
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
427
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
428
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
429
+
430
+ for (y = minY; y <= maxY; y++) {
431
+ guchar *thisLine = data + (rowstride * y);
432
+ guchar *pixel;
433
+
434
+ pixel = thisLine + (minX * pixWidth);
435
+
436
+ for (x = minX; x <= maxX; x++) {
437
+
438
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
439
+ int r, g, b, grey;
440
+
441
+ r = pixel[0];
442
+ g = pixel[1];
443
+ b = pixel[2];
444
+
445
+ if (alpha > 0) {
446
+ grey = alpha * ((double) (5 * (double) r + 60 * (double) g + 30 * (double) b)) / 100.0 +
447
+ (1 - alpha) * r;
448
+
449
+ pixel[0] = col((grey * alpha) + (1 - alpha) * r);
450
+ pixel[1] = col((grey * alpha) + (1 - alpha) * g);
451
+ pixel[2] = col((grey * alpha) + (1 - alpha) * b);
452
+ }
453
+
454
+ pixel += pixWidth;
455
+ }
456
+ }
457
+
458
+ }
459
+
460
+ static void highlight_blob(redeyeop_t *op, int blob_id, int colour) {
461
+ int y, x;
462
+ int minX, minY, maxX, maxY;
463
+ int hr, hg, hb;
464
+
465
+ hr = (colour >> 16) & 0xff;
466
+ hg = (colour >> 8) & 0xff;
467
+ hb = (colour) & 0xff;
468
+
469
+ minY = MAX(0, op->area.minY - 1);
470
+ maxY = MIN(op->area.maxY + 1, gdk_pixbuf_get_height(op->pixbuf) - 1);
471
+ minX = MAX(0, op->area.minX - 1);
472
+ maxX = MIN(op->area.maxX + 1, gdk_pixbuf_get_width(op->pixbuf) - 1);
473
+
474
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
475
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
476
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
477
+
478
+ for (y = minY; y <= maxY; y++) {
479
+ guchar *thisLine = data + (rowstride * y);
480
+ guchar *pixel;
481
+
482
+ pixel = thisLine + (minX * pixWidth);
483
+
484
+ for (x = minX; x <= maxX; x++) {
485
+
486
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
487
+ int r, g, b;
488
+
489
+ r = (pixel[0]);
490
+ g = (pixel[1]);
491
+ b = (pixel[2]);
492
+
493
+ if (alpha > 0) {
494
+
495
+ pixel[0] = col((1 - alpha) * r + (alpha * hr));
496
+ pixel[1] = col((1 - alpha) * g + (alpha * hg));
497
+ pixel[2] = col((1 - alpha) * b + (alpha * hb));
498
+ }
499
+
500
+ pixel += pixWidth;
501
+ }
502
+ }
503
+
504
+ }
505
+
506
+ static void preview_blob(redeyeop_t *op, int blob_id, int colour, gboolean reset_preview) {
507
+ int y, x;
508
+ int minX, minY, maxX, maxY;
509
+ int hr, hg, hb;
510
+
511
+ redeye_preview(op, reset_preview);
512
+
513
+ hr = (colour >> 16) & 0xff;
514
+ hg = (colour >> 8) & 0xff;
515
+ hb = (colour) & 0xff;
516
+
517
+ minY = 0;
518
+ maxY = gdk_pixbuf_get_height(op->preview) - 1;
519
+ minX = 0;
520
+ maxX = gdk_pixbuf_get_width(op->preview) - 1;
521
+
522
+ guchar *data = gdk_pixbuf_get_pixels(op->preview);
523
+ int rowstride = gdk_pixbuf_get_rowstride(op->preview);
524
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->preview) ? 4 : 3;
525
+
526
+ for (y = minY; y <= maxY; y++) {
527
+ guchar *thisLine = data + (rowstride * y);
528
+ guchar *pixel;
529
+
530
+ pixel = thisLine + (minX * pixWidth);
531
+
532
+ for (x = minX; x <= maxX; x++) {
533
+
534
+ double alpha = alpha_level_for_pixel(op, x + op->area.minX, y + op->area.minY, blob_id);
535
+ int r, g, b;
536
+
537
+ r = (pixel[0]);
538
+ g = (pixel[1]);
539
+ b = (pixel[2]);
540
+
541
+ if (alpha > 0) {
542
+
543
+ pixel[0] = col((1 - alpha) * r + (alpha * hr));
544
+ pixel[1] = col((1 - alpha) * g + (alpha * hg));
545
+ pixel[2] = col((1 - alpha) * b + (alpha * hb));
546
+ }
547
+
548
+ pixel += pixWidth;
549
+ }
550
+ }
551
+
552
+ }
553
+
554
+ /* Code */
555
+
556
+ static VALUE
557
+ PixbufUtils_CLASS_contrast(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_adjust OPTIONAL_ATTR) {
558
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
559
+ GdkPixbuf *src;
560
+ GdkPixbuf *__orig_src;
561
+ int adjust;
562
+ int __orig_adjust;
563
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
564
+ __orig_adjust = adjust = NUM2INT(__v_adjust);
565
+
566
+ IGNORE(self);
567
+ do {
568
+ __p_retval = unref_pixbuf((pixbuf_adjust_contrast(src, gdk_pixbuf_copy(src), adjust)));
569
+ goto out;
570
+ }
571
+ while (0);
572
+ out:;
573
+ return __p_retval;
574
+ }
575
+
576
+ static VALUE
577
+ PixbufUtils_CLASS_brightness(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_adjust OPTIONAL_ATTR) {
578
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
579
+ GdkPixbuf *src;
580
+ GdkPixbuf *__orig_src;
581
+ int adjust;
582
+ int __orig_adjust;
583
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
584
+ __orig_adjust = adjust = NUM2INT(__v_adjust);
585
+
586
+ IGNORE(self);
587
+ do {
588
+ __p_retval = unref_pixbuf((pixbuf_adjust_brightness(src, gdk_pixbuf_copy(src), adjust)));
589
+ goto out;
590
+ }
591
+ while (0);
592
+ out:;
593
+ return __p_retval;
594
+ }
595
+
596
+ static VALUE
597
+ PixbufUtils_CLASS_filter(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE filter OPTIONAL_ATTR,
598
+ VALUE __v_divisor OPTIONAL_ATTR) {
599
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
600
+ GdkPixbuf *src;
601
+ GdkPixbuf *__orig_src;
602
+ double divisor;
603
+ double __orig_divisor;
604
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
605
+ Check_Type(filter, T_ARRAY);
606
+ __orig_divisor = divisor = NUM2DBL(__v_divisor);
607
+
608
+ do {
609
+ long matrix_size = RARRAY_LEN(filter), i;
610
+ int len;
611
+ double *matrix;
612
+ IGNORE(self);
613
+ len = (int) sqrt((double) matrix_size);
614
+ if ((len * len) != matrix_size) {
615
+ rb_raise(rb_eArgError, "Invalid matrix size - sqrt(%li)*sqrt(%li) != %li", matrix_size, matrix_size,
616
+ matrix_size);
617
+ }
618
+ matrix = ALLOCA_N(
619
+ double, matrix_size);
620
+ for (i = 0;
621
+ i < matrix_size;
622
+ i++) {
623
+ matrix[i] = NUM2DBL(RARRAY_PTR(filter)[i]);
624
+ }
625
+ do {
626
+ __p_retval = unref_pixbuf((pixbuf_convolution_matrix(src, gdk_pixbuf_copy(src), len, matrix, divisor)));
627
+ goto out;
628
+ }
629
+ while (0);
630
+
631
+ } while (0);
632
+
633
+ out:;
634
+ return __p_retval;
635
+ }
636
+
637
+ static VALUE
638
+ PixbufUtils_CLASS_rotate(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_angle OPTIONAL_ATTR) {
639
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
640
+ GdkPixbuf *src;
641
+ GdkPixbuf *__orig_src;
642
+ int angle;
643
+ int __orig_angle;
644
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
645
+ __orig_angle = angle = NUM2INT(__v_angle);
646
+
647
+ IGNORE(self);
648
+ g_assert(angle == 0 || angle == 90 || angle == 180 || angle == 270);
649
+ do {
650
+ __p_retval = unref_pixbuf((pixbuf_rotate(src, (rotate_angle_t) angle)));
651
+ goto out;
652
+ }
653
+ while (0);
654
+ out:;
655
+ return __p_retval;
656
+ }
657
+
658
+
659
+ static VALUE
660
+ PixbufUtils_CLASS_gamma(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_level OPTIONAL_ATTR) {
661
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
662
+ GdkPixbuf *src;
663
+ GdkPixbuf *__orig_src;
664
+ double level;
665
+ double __orig_level;
666
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
667
+ __orig_level = level = NUM2DBL(__v_level);
668
+
669
+ IGNORE(self);
670
+ do {
671
+ __p_retval = unref_pixbuf((pixbuf_gamma(src, gdk_pixbuf_copy(src), level)));
672
+ goto out;
673
+ }
674
+ while (0);
675
+ out:;
676
+ return __p_retval;
677
+ }
678
+
679
+ static VALUE
680
+ PixbufUtils_CLASS_tint(int __p_argc, VALUE *__p_argv, VALUE self) {
681
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
682
+ VALUE __v_src = Qnil;
683
+ GdkPixbuf *src;
684
+ GdkPixbuf *__orig_src;
685
+ VALUE __v_r = Qnil;
686
+ int r;
687
+ int __orig_r;
688
+ VALUE __v_g = Qnil;
689
+ int g;
690
+ int __orig_g;
691
+ VALUE __v_b = Qnil;
692
+ int b;
693
+ int __orig_b;
694
+ VALUE __v_alpha = Qnil;
695
+ int alpha;
696
+ int __orig_alpha;
697
+
698
+ /* Scan arguments */
699
+ rb_scan_args(__p_argc, __p_argv, "41", &__v_src, &__v_r, &__v_g, &__v_b, &__v_alpha);
700
+
701
+ /* Set defaults */
702
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
703
+
704
+ __orig_r = r = NUM2INT(__v_r);
705
+
706
+ __orig_g = g = NUM2INT(__v_g);
707
+
708
+ __orig_b = b = NUM2INT(__v_b);
709
+
710
+ if (__p_argc > 4)
711
+ __orig_alpha = alpha = NUM2INT(__v_alpha);
712
+ else
713
+ alpha = 255;
714
+
715
+ IGNORE(self);
716
+ do {
717
+ __p_retval = unref_pixbuf((pixbuf_tint(src, gdk_pixbuf_copy(src), r, g, b, alpha)));
718
+ goto out;
719
+ }
720
+ while (0);
721
+ out:;
722
+ return __p_retval;
723
+ }
724
+
725
+ static VALUE
726
+ PixbufUtils_CLASS_mask(VALUE self OPTIONAL_ATTR, VALUE __v_src OPTIONAL_ATTR, VALUE __v_mask OPTIONAL_ATTR) {
727
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
728
+ GdkPixbuf *src;
729
+ GdkPixbuf *__orig_src;
730
+ GdkPixbuf *mask;
731
+ GdkPixbuf *__orig_mask;
732
+ __orig_src = src = GDK_PIXBUF(RVAL2GOBJ(__v_src));
733
+ __orig_mask = mask = GDK_PIXBUF(RVAL2GOBJ(__v_mask));
734
+
735
+ IGNORE(self);
736
+ do {
737
+ __p_retval = unref_pixbuf((pixbuf_mask(src, mask)));
738
+ goto out;
739
+ }
740
+ while (0);
741
+ out:;
742
+ return __p_retval;
743
+ }
744
+
745
+ static VALUE
746
+ RedEye___alloc__(VALUE self OPTIONAL_ATTR) {
747
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
748
+
749
+ do {
750
+ __p_retval = Data_Wrap_Struct(self, NULL, free_redeye, new_redeye());
751
+ goto out;
752
+ }
753
+ while (0);
754
+ out:
755
+ return __p_retval;
756
+ }
757
+
758
+ static VALUE
759
+ RedEye_initialize(VALUE self OPTIONAL_ATTR, VALUE __v_pixbuf OPTIONAL_ATTR, VALUE __v_minX OPTIONAL_ATTR, VALUE __v_minY
760
+ OPTIONAL_ATTR, VALUE __v_maxX OPTIONAL_ATTR, VALUE __v_maxY OPTIONAL_ATTR) {
761
+ GdkPixbuf *pixbuf;
762
+ GdkPixbuf *__orig_pixbuf;
763
+ int minX;
764
+ int __orig_minX;
765
+ int minY;
766
+ int __orig_minY;
767
+ int maxX;
768
+ int __orig_maxX;
769
+ int maxY;
770
+ int __orig_maxY;
771
+ __orig_pixbuf = pixbuf = GDK_PIXBUF(RVAL2GOBJ(__v_pixbuf));
772
+ __orig_minX = minX = NUM2INT(__v_minX);
773
+ __orig_minY = minY = NUM2INT(__v_minY);
774
+ __orig_maxX = maxX = NUM2INT(__v_maxX);
775
+ __orig_maxY = maxY = NUM2INT(__v_maxY);
776
+
777
+ do {
778
+ redeyeop_t *op;
779
+ Data_Get_Struct(self, redeyeop_t, op);
780
+ op->pixbuf = pixbuf;
781
+ op->preview = NULL;
782
+ g_object_ref(op->pixbuf);
783
+ op->area.minX = minX;
784
+ op->area.maxX = maxX;
785
+ op->area.minY = minY;
786
+ op->area.maxY = maxY;
787
+ op->area.width = maxX - minX + 1;
788
+ op->area.height = maxY - minY + 1;
789
+ g_assert(op->pixbuf != NULL);
790
+ g_assert(op->area.maxX <= gdk_pixbuf_get_width(op->pixbuf));
791
+ g_assert(op->area.minX >= 0);
792
+ g_assert(op->area.minX < op->area.maxX);
793
+ g_assert(op->area.maxY <= gdk_pixbuf_get_height(op->pixbuf));
794
+ g_assert(op->area.minY >= 0);
795
+ g_assert(op->area.minY < op->area.maxY);
796
+ op->mask = ALLOC_N(
797
+ int, op->area.width * op->area.height);
798
+ op->regions.data = ALLOC_N(
799
+ int, op->area.width * op->area.height);
800
+ op->regions.region = ALLOC_N(region_info, NO_REGIONS_DEFAULT);
801
+ op->regions.len = 0;
802
+ op->regions.size = NO_REGIONS_DEFAULT;
803
+
804
+ } while (0);
805
+
806
+ ;
807
+ return Qnil;
808
+ }
809
+
810
+ static VALUE
811
+ RedEye_identify_blobs(int __p_argc, VALUE *__p_argv, VALUE self) {
812
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
813
+ VALUE __v_green_sensitivity = Qnil;
814
+ double green_sensitivity;
815
+ double __orig_green_sensitivity;
816
+ VALUE __v_blue_sensitivity = Qnil;
817
+ double blue_sensitivity;
818
+ double __orig_blue_sensitivity;
819
+ VALUE __v_min_red_val = Qnil;
820
+ int min_red_val;
821
+ int __orig_min_red_val;
822
+
823
+ /* Scan arguments */
824
+ rb_scan_args(__p_argc, __p_argv, "03", &__v_green_sensitivity, &__v_blue_sensitivity, &__v_min_red_val);
825
+
826
+ /* Set defaults */
827
+ if (__p_argc > 0)
828
+ __orig_green_sensitivity = green_sensitivity = NUM2DBL(__v_green_sensitivity);
829
+ else
830
+ green_sensitivity = 2.0;
831
+
832
+ if (__p_argc > 1)
833
+ __orig_blue_sensitivity = blue_sensitivity = NUM2DBL(__v_blue_sensitivity);
834
+ else
835
+ blue_sensitivity = 0.0;
836
+
837
+ if (__p_argc > 2)
838
+ __orig_min_red_val = min_red_val = NUM2INT(__v_min_red_val);
839
+ else
840
+ min_red_val = MIN_RED_VAL;
841
+
842
+ do {
843
+ redeyeop_t *op;
844
+ Data_Get_Struct(self, redeyeop_t, op);
845
+ MEMZERO(op->mask,
846
+ int, op->area.width * op->area.height);
847
+ MEMZERO(op->regions.data,
848
+ int, op->area.width * op->area.height);
849
+ identify_possible_redeye_pixels(op, green_sensitivity, blue_sensitivity, min_red_val);
850
+ identify_blob_groupings(op);
851
+ volatile VALUE ary =
852
+ rb_ary_new2(op->regions.len);
853
+ int i;
854
+ for (i = MIN_ID;
855
+ i < op->regions.len;
856
+ i++) {
857
+ region_info *r =
858
+ &op->regions.region[i];
859
+ /* Ignore CCD noise */ if (r->noPixels < 2) continue;
860
+ rb_ary_push(ary, rb_struct_new(structRegion, self, INT2NUM(i), INT2NUM(r->minX), INT2NUM(r->minY),
861
+ INT2NUM(r->maxX), INT2NUM(r->maxY), INT2NUM(r->width), INT2NUM(r->height),
862
+ INT2NUM(r->noPixels)));
863
+ }
864
+ do {
865
+ __p_retval = ary;
866
+ goto out;
867
+ }
868
+ while (0);
869
+
870
+ } while (0);
871
+
872
+ out:
873
+ return __p_retval;
874
+ }
875
+
876
+ static VALUE
877
+ RedEye_correct_blob(VALUE self OPTIONAL_ATTR, VALUE __v_blob_id OPTIONAL_ATTR) {
878
+ int blob_id;
879
+ int __orig_blob_id;
880
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
881
+
882
+ do {
883
+ redeyeop_t *op;
884
+ Data_Get_Struct(self, redeyeop_t, op);
885
+ if (op->regions.len <= blob_id)
886
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
887
+ desaturate_blob(op, blob_id);
888
+
889
+ } while (0);
890
+
891
+ return Qnil;
892
+ }
893
+
894
+ static VALUE
895
+ RedEye_highlight_blob(int __p_argc, VALUE *__p_argv, VALUE self) {
896
+ VALUE __v_blob_id = Qnil;
897
+ int blob_id;
898
+ int __orig_blob_id;
899
+ VALUE __v_col = Qnil;
900
+ int col;
901
+ int __orig_col;
902
+
903
+ /* Scan arguments */
904
+ rb_scan_args(__p_argc, __p_argv, "11", &__v_blob_id, &__v_col);
905
+
906
+ /* Set defaults */
907
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
908
+
909
+ if (__p_argc > 1)
910
+ __orig_col = col = NUM2INT(__v_col);
911
+ else
912
+ col = 0x00ff00;
913
+
914
+ do {
915
+ redeyeop_t *op;
916
+ Data_Get_Struct(self, redeyeop_t, op);
917
+ if (op->regions.len <= blob_id)
918
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
919
+ highlight_blob(op, blob_id, col);
920
+
921
+ } while (0);
922
+
923
+ return Qnil;
924
+ }
925
+
926
+ static VALUE
927
+ RedEye_preview_blob(int __p_argc, VALUE *__p_argv, VALUE self) {
928
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
929
+ VALUE __v_blob_id = Qnil;
930
+ int blob_id;
931
+ int __orig_blob_id;
932
+ VALUE __v_col = Qnil;
933
+ int col;
934
+ int __orig_col;
935
+ VALUE __v_reset_preview = Qnil;
936
+ gboolean reset_preview;
937
+ gboolean __orig_reset_preview;
938
+
939
+ /* Scan arguments */
940
+ rb_scan_args(__p_argc, __p_argv, "12", &__v_blob_id, &__v_col, &__v_reset_preview);
941
+
942
+ /* Set defaults */
943
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
944
+
945
+ if (__p_argc > 1)
946
+ __orig_col = col = NUM2INT(__v_col);
947
+ else
948
+ col = 0x00ff00;
949
+
950
+ if (__p_argc > 2)
951
+ __orig_reset_preview = reset_preview = RTEST(__v_reset_preview);
952
+ else
953
+ reset_preview = TRUE;
954
+
955
+ do {
956
+ redeyeop_t *op;
957
+ Data_Get_Struct(self, redeyeop_t, op);
958
+ if (op->regions.len <= blob_id)
959
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
960
+ preview_blob(op, blob_id, col, reset_preview);
961
+ do {
962
+ __p_retval = GOBJ2RVAL(GDK_PIXBUF(op->preview));
963
+ goto out;
964
+ }
965
+ while (0);
966
+
967
+ } while (0);
968
+
969
+ out:
970
+ return __p_retval;
971
+ }
972
+
973
+ static VALUE
974
+ RedEye_preview(VALUE self OPTIONAL_ATTR) {
975
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
976
+
977
+ do {
978
+ redeyeop_t *op;
979
+ Data_Get_Struct(self, redeyeop_t, op);
980
+ do {
981
+ __p_retval = GOBJ2RVAL(GDK_PIXBUF(redeye_preview(op, FALSE)));
982
+ goto out;
983
+ }
984
+ while (0);
985
+
986
+ } while (0);
987
+
988
+ out:
989
+ return __p_retval;
990
+ }
991
+
992
+ static VALUE
993
+ RedEye_pixbuf(VALUE self OPTIONAL_ATTR) {
994
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
995
+
996
+ do {
997
+ redeyeop_t *op;
998
+ Data_Get_Struct(self, redeyeop_t, op);
999
+ do {
1000
+ __p_retval = GOBJ2RVAL(GDK_PIXBUF(op->pixbuf));
1001
+ goto out;
1002
+ }
1003
+ while (0);
1004
+
1005
+ } while (0);
1006
+
1007
+ out:
1008
+ return __p_retval;
1009
+ }
1010
+
1011
+ static VALUE
1012
+ Region_ratio(VALUE self OPTIONAL_ATTR) {
1013
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
1014
+
1015
+ int width, height;
1016
+ double min, max, ratio;
1017
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
1018
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
1019
+ min = (double) MIN(width, height);
1020
+ max = (double) MAX(width, height);
1021
+ ratio = (min / max);
1022
+ do {
1023
+ __p_retval = rb_float_new(ratio);
1024
+ goto out;
1025
+ }
1026
+ while (0);
1027
+ out:
1028
+ return __p_retval;
1029
+ }
1030
+
1031
+ static VALUE
1032
+ Region_density(VALUE self OPTIONAL_ATTR) {
1033
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
1034
+
1035
+ do {
1036
+ int noPixels, width, height;
1037
+ double density;
1038
+ noPixels = NUM2INT(rb_struct_getmember(self, rb_intern("noPixels")));
1039
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
1040
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
1041
+ density = ((double) noPixels / (double) (width * height));
1042
+ do {
1043
+ __p_retval = rb_float_new(density);
1044
+ goto out;
1045
+ }
1046
+ while (0);
1047
+
1048
+ } while (0);
1049
+
1050
+ out:
1051
+ return __p_retval;
1052
+ }
1053
+
1054
+ static VALUE
1055
+ Region_squareish_query(int __p_argc, VALUE *__p_argv, VALUE self) {
1056
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
1057
+ VALUE __v_min_ratio = Qnil;
1058
+ double min_ratio;
1059
+ double __orig_min_ratio;
1060
+ VALUE __v_min_density = Qnil;
1061
+ double min_density;
1062
+ double __orig_min_density;
1063
+
1064
+ /* Scan arguments */
1065
+ rb_scan_args(__p_argc, __p_argv, "02", &__v_min_ratio, &__v_min_density);
1066
+
1067
+ /* Set defaults */
1068
+ if (__p_argc > 0)
1069
+ __orig_min_ratio = min_ratio = NUM2DBL(__v_min_ratio);
1070
+ else
1071
+ min_ratio = 0.5;
1072
+
1073
+ if (__p_argc > 1)
1074
+ __orig_min_density = min_density = NUM2DBL(__v_min_density);
1075
+ else
1076
+ min_density = 0.5;
1077
+
1078
+ int noPixels, width, height;
1079
+ double min, max, ratio, density;
1080
+ noPixels = NUM2INT(rb_struct_getmember(self, rb_intern("noPixels")));
1081
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
1082
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
1083
+ min = (double) MIN(width, height);
1084
+ max = (double) MAX(width, height);
1085
+ ratio = (min / max);
1086
+ density = ((double) noPixels / (double) (width * height));
1087
+ do {
1088
+ __p_retval = ((((ratio >= min_ratio) && (density > min_density))) ? Qtrue : Qfalse);
1089
+ goto out;
1090
+ }
1091
+ while (0);
1092
+ out:
1093
+ return __p_retval;
1094
+ }
1095
+
1096
+ /* Init */
1097
+ void
1098
+ Init_morandi_native(void) {
1099
+ mMorandiNative = rb_define_module("MorandiNative");
1100
+ mPixbufUtils = rb_define_module_under(mMorandiNative, "PixbufUtils");
1101
+ rb_define_singleton_method(mPixbufUtils, "contrast", PixbufUtils_CLASS_contrast, 2);
1102
+ rb_define_singleton_method(mPixbufUtils, "brightness", PixbufUtils_CLASS_brightness, 2);
1103
+ rb_define_singleton_method(mPixbufUtils, "filter", PixbufUtils_CLASS_filter, 3);
1104
+ rb_define_singleton_method(mPixbufUtils, "rotate", PixbufUtils_CLASS_rotate, 2);
1105
+ rb_define_singleton_method(mPixbufUtils, "gamma", PixbufUtils_CLASS_gamma, 2);
1106
+ rb_define_singleton_method(mPixbufUtils, "tint", PixbufUtils_CLASS_tint, -1);
1107
+ rb_define_singleton_method(mPixbufUtils, "mask", PixbufUtils_CLASS_mask, 2);
1108
+
1109
+
1110
+
1111
+ cRedEye = rb_define_class_under(mMorandiNative, "RedEye", rb_cObject);
1112
+ rb_define_alloc_func(cRedEye, RedEye___alloc__);
1113
+ rb_define_method(cRedEye, "initialize", RedEye_initialize, 5);
1114
+ rb_define_method(cRedEye, "identify_blobs", RedEye_identify_blobs, -1);
1115
+ rb_define_method(cRedEye, "correct_blob", RedEye_correct_blob, 1);
1116
+ rb_define_method(cRedEye, "highlight_blob", RedEye_highlight_blob, -1);
1117
+ rb_define_method(cRedEye, "preview_blob", RedEye_preview_blob, -1);
1118
+ rb_define_method(cRedEye, "preview", RedEye_preview, 0);
1119
+ rb_define_method(cRedEye, "pixbuf", RedEye_pixbuf, 0);
1120
+ structRegion = rb_struct_define_under(cRedEye, "Region", "op", "id", "minX", "minY", "maxX", "maxY", "width", "height", "noPixels",
1121
+ NULL);
1122
+ // rb_define_const(cRedEye, "Region", structRegion);
1123
+ rb_define_method(structRegion, "ratio", Region_ratio, 0);
1124
+ rb_define_method(structRegion, "density", Region_density, 0);
1125
+ rb_define_method(structRegion, "squareish?", Region_squareish_query, -1);
1126
+ }