morandi 0.12.1 → 0.99.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }