redeye 1.0.0

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,650 @@
1
+ %pkg-config gtk+-2.0
2
+ %include gdk-pixbuf/gdk-pixbuf.h
3
+
4
+
5
+ %map VALUE > GdkPixbuf* : GDK_PIXBUF(RVAL2GOBJ(%%))
6
+ %map GdkPixbuf* > VALUE : GOBJ2RVAL(GDK_PIXBUF(%%))
7
+ %map unref_pixbuf > VALUE : unref_pixbuf((%%))
8
+
9
+ %{
10
+
11
+ #define assert(x) if (!(x)) { rb_raise(rb_eRuntimeError, "Assertion: '%s' failed.", #x); }
12
+
13
+ typedef struct {
14
+ char red,green,blue;
15
+ } rgb_t;
16
+
17
+ typedef struct {
18
+ int minX, maxX, minY, maxY;
19
+ int width, height;
20
+ int noPixels, mergeWith;
21
+ } region_info;
22
+
23
+ typedef struct {
24
+ struct {
25
+ int minX, maxX, minY, maxY;
26
+ int width, height;
27
+ } area;
28
+ struct {
29
+ int *data;
30
+ region_info *region;
31
+ int len, size;
32
+ } regions;
33
+ int *mask;
34
+ GdkPixbuf *pixbuf, *preview;
35
+ } redeyeop_t;
36
+
37
+ #define MIN_RED_VAL 20
38
+
39
+ static inline VALUE
40
+ unref_pixbuf(GdkPixbuf *pixbuf)
41
+ {
42
+ volatile VALUE pb = Qnil;
43
+
44
+ pb = GOBJ2RVAL(pixbuf);
45
+
46
+ g_object_unref(pixbuf);
47
+
48
+ return pb;
49
+ }
50
+
51
+ static void identify_possible_redeye_pixels(redeyeop_t *op,
52
+ double green_sensitivity, double blue_sensitivity,
53
+ int min_red_val)
54
+ {
55
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
56
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
57
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
58
+
59
+ int y, ry = 0, x, rx = 0;
60
+ for ( y = op->area.minY; y < op->area.maxY; y++ )
61
+ {
62
+ guchar *thisLine = data + (rowstride * y);
63
+ guchar *pixel;
64
+
65
+ pixel = thisLine + (op->area.minX * pixWidth);
66
+ rx = 0;
67
+
68
+ for ( x = op->area.minX; x < op->area.maxX; x++ )
69
+ {
70
+
71
+ int r,g,b;
72
+
73
+ r = pixel[0];
74
+ g = pixel[1];
75
+ b = pixel[2];
76
+
77
+ gboolean threshMet;
78
+
79
+ threshMet = (((double)r) > (green_sensitivity * (double)g)) &&
80
+ (((double)r) > (blue_sensitivity * (double)b)) &&
81
+ (r > min_red_val);
82
+
83
+ if(threshMet)
84
+ op->mask[ rx + ry ] = r;
85
+ else
86
+ op->mask[ rx + ry ] = 0; /* MEMZERO should have done its job ? */
87
+
88
+ pixel += pixWidth;
89
+ rx ++;
90
+ }
91
+
92
+ ry += op->area.width;
93
+ }
94
+ }
95
+
96
+
97
+ inline int group_at(redeyeop_t *op, int px, int py)
98
+ {
99
+ int index, region;
100
+
101
+ if (px < 0 || py < 0)
102
+ return 0;
103
+
104
+ index = px + ( py * op->area.width );
105
+
106
+ if (index < 0)
107
+ return 0;
108
+ if (index > (op->area.width * op->area.height))
109
+ return 0;
110
+
111
+ region = op->regions.data[ index ];
112
+ if (region > 0) {
113
+ if (op->regions.region[ region ].mergeWith) {
114
+ return op->regions.region[ region ].mergeWith;
115
+ } else {
116
+ return region;
117
+ }
118
+ } else {
119
+ return 0;
120
+ }
121
+ }
122
+
123
+ #define group_for(x,y) group_at(op, x, y)
124
+
125
+ static void identify_blob_groupings(redeyeop_t *op)
126
+ {
127
+ volatile int next_blob_id = 1, blob_id, y, x;
128
+
129
+
130
+ for( y = 0; y < op->area.height; y++ )
131
+ {
132
+ for ( x = 0; x < op->area.width; x++ )
133
+ {
134
+ if (op->mask[ x + (y * op->area.width) ] > 0) {
135
+ gboolean existing = FALSE;
136
+ int sx, sy, group = 0;
137
+ // Target pixel is true
138
+ blob_id = 0;
139
+
140
+ for (sy = y; sy >= y - 1; sy --) {
141
+ sx = (sy == y) ? x : x + 1;
142
+ for (; sx >= (x - 1); sx --) {
143
+ /*if ((sx >= x) && (sy >= y))
144
+ goto blob_scan_done;*/
145
+
146
+ if (sx >= 0 && sy >= 0)
147
+ group = group_for(sx, sy);
148
+
149
+ if (group) {
150
+ existing = TRUE;
151
+ if (blob_id) {
152
+ int target = MIN(blob_id, group);
153
+ int from = MAX(blob_id, group);
154
+
155
+ if (op->regions.region[target].mergeWith > 0) {
156
+ // Already merged
157
+ target = op->regions.region[target].mergeWith;
158
+ }
159
+ op->regions.region[from].mergeWith = target;
160
+
161
+ // Merge blob_id & group
162
+ }
163
+ blob_id = group;
164
+ }
165
+ }
166
+ }
167
+
168
+ if (blob_id == 0)
169
+ { // Allocate new group
170
+ blob_id = next_blob_id;
171
+ op->regions.region[blob_id].minX = x;
172
+ op->regions.region[blob_id].maxX = x;
173
+ op->regions.region[blob_id].minY = y;
174
+ op->regions.region[blob_id].maxY = y;
175
+ op->regions.region[blob_id].width = 1;
176
+ op->regions.region[blob_id].height = 1;
177
+ op->regions.region[blob_id].noPixels = 1;
178
+ op->regions.region[blob_id].mergeWith = 0;
179
+
180
+ next_blob_id ++;
181
+ op->regions.len = next_blob_id;
182
+
183
+ if (next_blob_id >= op->regions.size) {
184
+ int extra, new_size;
185
+
186
+ /*
187
+ * Realloc in increasingly large chunks to reduce memory fragmentation
188
+ */
189
+ extra = op->regions.size;
190
+ new_size = op->regions.size + extra;
191
+
192
+ REALLOC_N(op->regions.region, region_info, new_size);
193
+
194
+ op->regions.size = new_size;
195
+ }
196
+ }
197
+
198
+ if (existing)
199
+ {
200
+ op->regions.region[blob_id].minX = MIN(x, op->regions.region[blob_id].minX);
201
+ op->regions.region[blob_id].maxX = MAX(x, op->regions.region[blob_id].maxX);
202
+ op->regions.region[blob_id].minY = MIN(y, op->regions.region[blob_id].minY);
203
+ op->regions.region[blob_id].maxY = MAX(y, op->regions.region[blob_id].maxY);
204
+ op->regions.region[blob_id].width = op->regions.region[blob_id].maxX -
205
+ op->regions.region[blob_id].minX + 1;
206
+ op->regions.region[blob_id].height = op->regions.region[blob_id].maxY -
207
+ op->regions.region[blob_id].minY + 1;
208
+ op->regions.region[blob_id].noPixels ++;
209
+ }
210
+
211
+ op->regions.data[ x + (y * op->area.width) ] = blob_id;
212
+ }
213
+ }
214
+ }
215
+ /*FILE *fp = fopen("regions.txt","w");*/
216
+ for (y = 0; y < op->area.height; y++) {
217
+ for (x = 0; x < op->area.width; x++) {
218
+ int g = group_at(op, x, y); // Returns the merged group...
219
+ op->regions.data[ x + (y * op->area.width) ] = g;
220
+ /*
221
+ if (op->regions.len <= 0xf || 1)
222
+ {
223
+ if (g == 0)
224
+ fprintf(fp, " ");
225
+ else
226
+ fprintf(fp, "%x", g);
227
+ }
228
+ else
229
+ {
230
+ if (g == 0)
231
+ fprintf(fp, " ");
232
+ else
233
+ fprintf(fp, "%x ", g);
234
+ }*/
235
+ }
236
+ /*fprintf(fp, "\n");*/
237
+ }
238
+ /*fclose(fp);*/
239
+ }
240
+ #define NO_REGIONS_DEFAULT 20
241
+ #define MIN_ID 1
242
+
243
+
244
+ static redeyeop_t *new_redeye(void)
245
+ {
246
+ redeyeop_t *ptr = ALLOC(redeyeop_t);
247
+ MEMZERO(ptr, redeyeop_t, 1);
248
+ return ptr;
249
+ }
250
+
251
+ static void free_redeye(redeyeop_t *ptr)
252
+ {
253
+ if (ptr->mask)
254
+ free(ptr->mask);
255
+ if (ptr->regions.data);
256
+ free(ptr->regions.data);
257
+ if (ptr->regions.region);
258
+ free(ptr->regions.region);
259
+ if (ptr->pixbuf)
260
+ g_object_unref(ptr->pixbuf);
261
+ if (ptr->preview)
262
+ g_object_unref(ptr->preview);
263
+ free(ptr);
264
+ }
265
+
266
+
267
+ inline gboolean in_region(redeyeop_t *op, int x, int y, int blob_id)
268
+ {
269
+ int index;
270
+
271
+ if ( x < op->area.minX || x > op->area.maxX ||
272
+ y < op->area.minY || y > op->area.maxY )
273
+ return FALSE;
274
+
275
+ index = (x - op->area.minX) + ((y - op->area.minY) * op->area.width);
276
+
277
+ return op->regions.data[index] == blob_id;
278
+ }
279
+
280
+ inline double alpha_level_for_pixel(redeyeop_t *op, int x, int y, int blob_id)
281
+ {
282
+ int j = 0, c = 0, xm, ym;
283
+
284
+ if (in_region(op, x, y, blob_id))
285
+ return 1.0;
286
+
287
+ for ( xm = -2; xm <= 2; xm++ )
288
+ {
289
+ for ( ym = -2; ym <= 2; ym ++ )
290
+ {
291
+ c ++;
292
+ if (xm == 0 && ym == 0)
293
+ continue;
294
+ if (in_region(op, x+xm, y+ym, blob_id))
295
+ j ++;
296
+ }
297
+ }
298
+
299
+ return ((double)j)/((double)c);
300
+ }
301
+
302
+ inline char col(double val)
303
+ {
304
+ if (val < 0) return 0;
305
+ if (val > 255) return 255;
306
+ return val;
307
+
308
+ }
309
+
310
+ static GdkPixbuf *redeye_preview(redeyeop_t *op, gboolean reset)
311
+ {
312
+ int width, height;
313
+ width = op->area.width;
314
+ height = op->area.height;
315
+
316
+ if (width + op->area.minX > gdk_pixbuf_get_width(op->pixbuf)) {
317
+ width = gdk_pixbuf_get_width(op->pixbuf) - op->area.minX;
318
+ }
319
+ if (height + op->area.minY > gdk_pixbuf_get_height(op->pixbuf)) {
320
+ height = gdk_pixbuf_get_height(op->pixbuf) - op->area.minY;
321
+ }
322
+
323
+ if ( op->preview == NULL )
324
+ {
325
+ GdkPixbuf *sub = NULL;
326
+ sub = gdk_pixbuf_new_subpixbuf(op->pixbuf, op->area.minX, op->area.minY,
327
+ width, height);
328
+
329
+ op->preview = gdk_pixbuf_copy(sub);
330
+ g_object_unref(sub);
331
+ } else if (reset) {
332
+ gdk_pixbuf_copy_area(op->pixbuf, op->area.minX, op->area.minY,
333
+ width, height, op->preview, 0, 0);
334
+ }
335
+
336
+ return op->preview;
337
+ }
338
+
339
+ static void desaturate_blob(redeyeop_t *op, int blob_id)
340
+ {
341
+ int y, x;
342
+ int minX, minY, maxX, maxY;
343
+
344
+ minY = MAX(0, op->area.minY + op->regions.region[blob_id].minY - 1);
345
+ maxY = MIN(op->area.maxY + op->regions.region[blob_id].maxY + 1,
346
+ gdk_pixbuf_get_height(op->pixbuf)-1);
347
+ minX = MAX(0, op->area.minX + op->regions.region[blob_id].minX - 1);
348
+ maxX = MIN(op->area.maxX + op->regions.region[blob_id].maxX + 1,
349
+ gdk_pixbuf_get_width(op->pixbuf)-1);
350
+
351
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
352
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
353
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
354
+
355
+ for ( y = minY; y <= maxY; y++ )
356
+ {
357
+ guchar *thisLine = data + (rowstride * y);
358
+ guchar *pixel;
359
+
360
+ pixel = thisLine + (minX * pixWidth);
361
+
362
+ for ( x = minX; x <= maxX; x++ )
363
+ {
364
+
365
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
366
+ int r,g,b,grey;
367
+
368
+ r = pixel[0];
369
+ g = pixel[1];
370
+ b = pixel[2];
371
+
372
+ if (alpha > 0)
373
+ {
374
+ grey = alpha * ((double)( 5 * (double)r + 60 * (double)g + 30 * (double)b)) / 100.0 +
375
+ (1 - alpha) * r;
376
+
377
+ pixel[0] = col((grey * alpha) + (1-alpha) * r);
378
+ pixel[1] = col((grey * alpha) + (1-alpha) * g);
379
+ pixel[2] = col((grey * alpha) + (1-alpha) * b);
380
+ }
381
+
382
+ pixel += pixWidth;
383
+ }
384
+ }
385
+
386
+ }
387
+
388
+ static void highlight_blob(redeyeop_t *op, int blob_id, int colour)
389
+ {
390
+ int y, x;
391
+ int minX, minY, maxX, maxY;
392
+ int hr, hg, hb;
393
+
394
+ hr = (colour >> 16) & 0xff;
395
+ hg = (colour >> 8) & 0xff;
396
+ hb = (colour) & 0xff;
397
+
398
+ minY = MAX(0, op->area.minY - 1);
399
+ maxY = MIN(op->area.maxY + 1, gdk_pixbuf_get_height(op->pixbuf)-1);
400
+ minX = MAX(0, op->area.minX - 1);
401
+ maxX = MIN(op->area.maxX + 1, gdk_pixbuf_get_width(op->pixbuf)-1);
402
+
403
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
404
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
405
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
406
+
407
+ for ( y = minY; y <= maxY; y++ )
408
+ {
409
+ guchar *thisLine = data + (rowstride * y);
410
+ guchar *pixel;
411
+
412
+ pixel = thisLine + (minX * pixWidth);
413
+
414
+ for ( x = minX; x <= maxX; x++ )
415
+ {
416
+
417
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
418
+ int r,g,b;
419
+
420
+ r = (pixel[0]);
421
+ g = (pixel[1]);
422
+ b = (pixel[2]);
423
+
424
+
425
+ if (alpha > 0)
426
+ {
427
+
428
+ pixel[0] = col((1-alpha) * r + (alpha * hr));
429
+ pixel[1] = col((1-alpha) * g + (alpha * hg));
430
+ pixel[2] = col((1-alpha) * b + (alpha * hb));
431
+ }
432
+
433
+ pixel += pixWidth;
434
+ }
435
+ }
436
+
437
+ }
438
+
439
+
440
+ static void preview_blob(redeyeop_t *op, int blob_id, int colour, gboolean reset_preview)
441
+ {
442
+ int y, x;
443
+ int minX, minY, maxX, maxY;
444
+ int hr, hg, hb;
445
+
446
+ redeye_preview(op, reset_preview);
447
+
448
+ hr = (colour >> 16) & 0xff;
449
+ hg = (colour >> 8) & 0xff;
450
+ hb = (colour) & 0xff;
451
+
452
+ minY = 0;
453
+ maxY = gdk_pixbuf_get_height(op->preview)-1;
454
+ minX = 0;
455
+ maxX = gdk_pixbuf_get_width(op->preview)-1;
456
+
457
+ guchar *data = gdk_pixbuf_get_pixels(op->preview);
458
+ int rowstride = gdk_pixbuf_get_rowstride(op->preview);
459
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->preview) ? 4 : 3;
460
+
461
+ for ( y = minY; y <= maxY; y++ )
462
+ {
463
+ guchar *thisLine = data + (rowstride * y);
464
+ guchar *pixel;
465
+
466
+ pixel = thisLine + (minX * pixWidth);
467
+
468
+ for ( x = minX; x <= maxX; x++ )
469
+ {
470
+
471
+ double alpha = alpha_level_for_pixel(op, x + op->area.minX, y + op->area.minY, blob_id);
472
+ int r,g,b;
473
+
474
+ r = (pixel[0]);
475
+ g = (pixel[1]);
476
+ b = (pixel[2]);
477
+
478
+
479
+ if (alpha > 0)
480
+ {
481
+
482
+ pixel[0] = col((1-alpha) * r + (alpha * hr));
483
+ pixel[1] = col((1-alpha) * g + (alpha * hg));
484
+ pixel[2] = col((1-alpha) * b + (alpha * hb));
485
+ }
486
+
487
+ pixel += pixWidth;
488
+ }
489
+ }
490
+
491
+ }
492
+
493
+ %}
494
+
495
+ class RedEye
496
+ struct Region(op, id, minX, minY, maxX, maxY, width, height, noPixels)
497
+ def double:ratio
498
+ int width,height;
499
+ double min,max,ratio;
500
+ width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
501
+ height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
502
+ min = (double)MIN(width,height);
503
+ max = (double)MAX(width,height);
504
+ ratio = (min / max);
505
+ return ratio;
506
+ end
507
+ def double:density
508
+ int noPixels, width, height;
509
+ double density;
510
+
511
+ noPixels = <{VALUE>int:rb_struct_getmember(self, rb_intern("noPixels"))}>;
512
+ width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
513
+ height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
514
+ density = ((double)noPixels / (double)(width * height));
515
+
516
+ return density;
517
+ end
518
+ def gboolean:squareish?(double min_ratio = 0.5, double min_density = 0.5)
519
+ int noPixels, width, height;
520
+ double min, max, ratio, density;
521
+
522
+ noPixels = <{VALUE>int:rb_struct_getmember(self, rb_intern("noPixels"))}>;
523
+ width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
524
+ height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
525
+
526
+ min = (double)MIN(width,height);
527
+ max = (double)MAX(width,height);
528
+ ratio = (min / max);
529
+
530
+ density = ((double)noPixels / (double)(width * height));
531
+
532
+ return ((ratio >= min_ratio) && (density > min_density));
533
+ end
534
+ end
535
+
536
+ def __alloc__
537
+ return Data_Wrap_Struct(self, NULL, free_redeye, new_redeye());
538
+ end
539
+
540
+ def initialize(GdkPixbuf *pixbuf, int minX, int minY, int maxX, int maxY)
541
+ redeyeop_t *op;
542
+
543
+ Data_Get_Struct(self, redeyeop_t, op);
544
+
545
+ op->pixbuf = pixbuf;
546
+ op->preview = NULL;
547
+ g_object_ref(op->pixbuf);
548
+
549
+ op->area.minX = minX;
550
+ op->area.maxX = maxX;
551
+ op->area.minY = minY;
552
+ op->area.maxY = maxY;
553
+ op->area.width = maxX - minX + 1;
554
+ op->area.height = maxY - minY + 1;
555
+
556
+ assert(op->pixbuf != NULL);
557
+ assert(op->area.maxX <= gdk_pixbuf_get_width(op->pixbuf));
558
+ assert(op->area.minX >= 0);
559
+ assert(op->area.minX < op->area.maxX);
560
+ assert(op->area.maxY <= gdk_pixbuf_get_height(op->pixbuf));
561
+ assert(op->area.minY >= 0);
562
+ assert(op->area.minY < op->area.maxY);
563
+
564
+
565
+ op->mask = ALLOC_N(int, op->area.width * op->area.height);
566
+
567
+ op->regions.data = ALLOC_N(int, op->area.width * op->area.height);
568
+
569
+ op->regions.region = ALLOC_N(region_info, NO_REGIONS_DEFAULT);
570
+
571
+ op->regions.len = 0;
572
+ op->regions.size = NO_REGIONS_DEFAULT;
573
+ end
574
+
575
+ def identify_blobs(double green_sensitivity=2.0, double blue_sensitivity=0.0, int min_red_val = MIN_RED_VAL)
576
+ redeyeop_t *op;
577
+
578
+ Data_Get_Struct(self, redeyeop_t, op);
579
+
580
+ MEMZERO(op->mask, int, op->area.width * op->area.height);
581
+ MEMZERO(op->regions.data, int, op->area.width * op->area.height);
582
+
583
+ identify_possible_redeye_pixels(op, green_sensitivity, blue_sensitivity, min_red_val);
584
+ identify_blob_groupings(op);
585
+
586
+ volatile VALUE ary = rb_ary_new2(op->regions.len);
587
+ int i;
588
+ for (i = MIN_ID; i < op->regions.len; i++) {
589
+ region_info *r = &op->regions.region[i];
590
+ /* Ignore CCD noise */
591
+ if (r->noPixels < 2)
592
+ continue;
593
+ rb_ary_push(ary, rb_struct_new(structRegion, self, INT2NUM(i),
594
+ INT2NUM(r->minX), INT2NUM(r->minY), INT2NUM(r->maxX), INT2NUM(r->maxY),
595
+ INT2NUM(r->width), INT2NUM(r->height), INT2NUM(r->noPixels)));
596
+ }
597
+ return ary;
598
+ end
599
+
600
+ def correct_blob(int blob_id)
601
+ redeyeop_t *op;
602
+
603
+ Data_Get_Struct(self, redeyeop_t, op);
604
+
605
+ if (op->regions.len <= blob_id)
606
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
607
+
608
+
609
+ desaturate_blob(op, blob_id);
610
+ end
611
+
612
+ def highlight_blob(int blob_id, int col = 0x00ff00)
613
+ redeyeop_t *op;
614
+
615
+ Data_Get_Struct(self, redeyeop_t, op);
616
+
617
+ if (op->regions.len <= blob_id)
618
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
619
+
620
+ highlight_blob(op, blob_id, col);
621
+ end
622
+
623
+ def GdkPixbuf*:preview_blob(int blob_id, int col = 0x00ff00, gboolean reset_preview = TRUE)
624
+ redeyeop_t *op;
625
+
626
+ Data_Get_Struct(self, redeyeop_t, op);
627
+
628
+ if (op->regions.len <= blob_id)
629
+ rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
630
+
631
+ preview_blob(op, blob_id, col, reset_preview);
632
+
633
+ return op->preview;
634
+ end
635
+ def GdkPixbuf*:preview
636
+ redeyeop_t *op;
637
+
638
+ Data_Get_Struct(self, redeyeop_t, op);
639
+
640
+ return redeye_preview(op, FALSE);
641
+ end
642
+ def GdkPixbuf*:pixbuf
643
+ redeyeop_t *op;
644
+
645
+ Data_Get_Struct(self, redeyeop_t, op);
646
+
647
+ return op->pixbuf;
648
+ end
649
+ end
650
+