sobakasu-image_science 1.1.3

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,803 @@
1
+ /*
2
+ * Provides a clean and simple API to FreeImage.
3
+ */
4
+
5
+ #include "ruby.h"
6
+ #include "FreeImage.h"
7
+ #include <sys/stat.h>
8
+
9
+ #define GET_BITMAP(name) Data_Get_Struct(self, FIBITMAP, (name)); if (!(name)) rb_raise(rb_eTypeError, "Bitmap has already been freed")
10
+ #define RB_BOOL(value) (value ? Qtrue : Qfalse)
11
+
12
+ #ifdef HAVE_FREEIMAGE_ROTATE
13
+ #define ROTATE(bitmap, angle) FreeImage_Rotate(bitmap, angle, NULL)
14
+ #else
15
+ #define ROTATE(bitmap, angle) FreeImage_RotateClassic(bitmap, angle)
16
+ #endif
17
+
18
+ VALUE isc; /* ImageScience class */
19
+ VALUE isc_image_types; /* ImageScience::ImageTypes module */
20
+ VALUE isc_image_formats; /* ImageScience::ImageFormats module */
21
+ VALUE isc_image_filters; /* ImageScience::ImageFilters module */
22
+ VALUE isc_color_chan; /* ImageScience::ColorChannels module */
23
+ VALUE isc_color_types; /* ImageScience::ColorTypes module */
24
+ VALUE isc_ls_flags; /* ImageScience::LoadSaveFlags module */
25
+
26
+ static VALUE unload(FIBITMAP *bitmap) {
27
+ FreeImage_Unload(bitmap);
28
+ return Qnil;
29
+ }
30
+
31
+ static VALUE isc_alloc(VALUE klass)
32
+ {
33
+ VALUE obj = Data_Wrap_Struct(klass, NULL, unload, NULL);
34
+ DATA_PTR(obj) = NULL;
35
+ return obj;
36
+ }
37
+
38
+ static FREE_IMAGE_FORMAT get_fif(VALUE self) {
39
+ return FIX2INT(rb_iv_get(self, "@file_type"));
40
+ }
41
+
42
+ static VALUE wrap_and_yield(FIBITMAP *image, VALUE self, FREE_IMAGE_FORMAT fif) {
43
+ unsigned int self_is_class = rb_type(self) == T_CLASS;
44
+ VALUE klass = self_is_class ? self : CLASS_OF(self);
45
+ VALUE obj = isc_alloc(klass);
46
+
47
+ if(!self_is_class) fif = get_fif(self);
48
+
49
+ DATA_PTR(obj) = image;
50
+ rb_iv_set(obj, "@file_type", INT2FIX(fif));
51
+ return rb_yield(obj);
52
+ }
53
+
54
+ static void copy_icc_profile(FIBITMAP *from, FIBITMAP *to, FREE_IMAGE_FORMAT fif) {
55
+ if (fif != FIF_PNG && FreeImage_FIFSupportsICCProfiles(fif)) {
56
+ FIICCPROFILE *profile = FreeImage_GetICCProfile(from);
57
+ if (profile && profile->data) {
58
+ FreeImage_CreateICCProfile(to, profile->data, profile->size);
59
+ }
60
+ }
61
+ }
62
+
63
+ static void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) {
64
+ rb_raise(rb_eRuntimeError,
65
+ "FreeImage exception for type %s: %s",
66
+ (fif == FIF_UNKNOWN) ? "???" : FreeImage_GetFormatFromFIF(fif),
67
+ message);
68
+ }
69
+
70
+ /****** helper functions ****/
71
+
72
+ /*
73
+ * Prepare the given bitmap for saving in the specified format.
74
+ * If a new bitmap is required, returns a bitmap, else returns NULL.
75
+ */
76
+ static FIBITMAP *
77
+ _prepare_bitmap_for_save(FIBITMAP *bitmap, FREE_IMAGE_FORMAT fif)
78
+ {
79
+ FIBITMAP *new_bitmap = NULL;
80
+
81
+ // if (fif == FIF_PNG) FreeImage_DestroyICCProfile(bitmap);
82
+
83
+ if (fif == FIF_JPEG && FreeImage_GetBPP(bitmap) != 24) {
84
+ new_bitmap = FreeImage_ConvertTo24Bits(bitmap);
85
+ }
86
+ if (fif == FIF_GIF && FreeImage_GetBPP(bitmap) != 8) {
87
+ new_bitmap = FreeImage_ConvertTo8Bits(bitmap);
88
+ }
89
+
90
+ if (new_bitmap) copy_icc_profile(bitmap, new_bitmap, fif);
91
+
92
+ return new_bitmap;
93
+ }
94
+
95
+ /* called by isc_init, with_image, with_image_from_memory */
96
+ static VALUE isc_init2(VALUE self, VALUE image, VALUE flags_arg, int is_file)
97
+ {
98
+ char *img;
99
+ FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
100
+ DWORD image_data_length;
101
+ FIMEMORY *stream;
102
+ FIBITMAP *bitmap, *new_bitmap;
103
+ int flags, flags_given;
104
+
105
+ flags = NIL_P(flags_arg) ? 0 : FIX2INT(flags_arg);
106
+ flags_given = !NIL_P(flags_arg);
107
+
108
+ Check_Type(image, T_STRING);
109
+ img = RSTRING(image)->ptr;
110
+
111
+ if (is_file) {
112
+ /* argument is a file */
113
+
114
+ fif = FreeImage_GetFileType(img, 0);
115
+ if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(img);
116
+ if ((fif == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(fif)) {
117
+ rb_raise(rb_eTypeError, "Unknown file format");
118
+ }
119
+
120
+ if(!flags_given && fif == FIF_JPEG) flags = JPEG_ACCURATE;
121
+
122
+ bitmap = FreeImage_Load(fif, img, flags);
123
+ }
124
+ else {
125
+ /* attempt to read argument as image data */
126
+ image_data_length = RSTRING(image)->len;
127
+ stream = FreeImage_OpenMemory((BYTE *)img, image_data_length);
128
+
129
+ fif = FreeImage_GetFileTypeFromMemory(stream, 0);
130
+ if ((fif == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(fif)) {
131
+ rb_raise(rb_eTypeError, "Unknown file format");
132
+ }
133
+
134
+ if(!flags_given && fif == FIF_JPEG) flags = JPEG_ACCURATE;
135
+
136
+ bitmap = FreeImage_LoadFromMemory(fif, stream, flags);
137
+ FreeImage_CloseMemory(stream);
138
+ }
139
+
140
+ /* rotate bitmap according to Orientation EXIF tag */
141
+ if (bitmap) {
142
+ FITAG *tagValue = NULL;
143
+ double angle = 0;
144
+
145
+ FreeImage_GetMetadata(FIMD_EXIF_MAIN, bitmap, "Orientation", &tagValue);
146
+
147
+ switch (tagValue == NULL ? 0 : *((short *) FreeImage_GetTagValue(tagValue))) {
148
+ case 6:
149
+ angle = 270; break;
150
+ case 3:
151
+ angle = 180; break;
152
+ case 8:
153
+ angle = 90; break;
154
+ }
155
+
156
+ if (angle) {
157
+ new_bitmap = ROTATE(bitmap, angle);
158
+ FreeImage_Unload(bitmap);
159
+ bitmap = new_bitmap;
160
+ }
161
+ }
162
+
163
+ DATA_PTR(self) = bitmap;
164
+ rb_iv_set(self, "@file_type", INT2FIX(fif));
165
+
166
+ return self;
167
+ }
168
+
169
+ /****** Class methods ******/
170
+
171
+ /*
172
+ * call-seq:
173
+ * with_image(filename) { |img| ... }
174
+ *
175
+ * The top-level image loader opens +filename+ and then yields the image.
176
+ */
177
+ static VALUE with_image(VALUE klass, VALUE filename) {
178
+ VALUE self = isc_alloc(isc);
179
+ isc_init2(self, filename, Qnil, 1);
180
+ return rb_yield(self);
181
+ }
182
+
183
+ /*
184
+ * call-seq:
185
+ * with_image_from_memory(image_data) { |img| ... }
186
+ *
187
+ * The top-level image loader, opens an image from the string +image_data+
188
+ * and then yields the image.
189
+ */
190
+ static VALUE with_image_from_memory(VALUE klass, VALUE image_data) {
191
+ VALUE self = isc_alloc(isc);
192
+ isc_init2(self, image_data, Qnil, 0);
193
+ return rb_yield(self);
194
+ }
195
+
196
+ /*
197
+ * Returns the FreeImage library version.
198
+ */
199
+ static VALUE get_version(VALUE self) {
200
+ const char *version = FreeImage_GetVersion();
201
+ return rb_str_new2(version);
202
+ }
203
+
204
+ /*
205
+ * Orders FreeImage to analyze the bitmap signature. The method then
206
+ * returns one of the predefined ImageScience::ImageFormats or a bitmap
207
+ * identification number registered by a plugin. The size parameter is
208
+ * currently not used and can be set to 0.
209
+ */
210
+ static VALUE file_type(VALUE self, VALUE filename) {
211
+ char * input = RSTRING(filename)->ptr;
212
+ FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
213
+
214
+ fif = FreeImage_GetFileType(input, 0);
215
+ if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(input);
216
+ return (fif == FIF_UNKNOWN) ? Qnil : INT2FIX(fif);
217
+ }
218
+
219
+ /*********** Instance methods ***********/
220
+
221
+ /*
222
+ * call-seq:
223
+ * buffer()
224
+ * buffer() { |string| ... }
225
+ * buffer(format, flags = 0)
226
+ * buffer(format, flags = 0) { |string| ... }
227
+ *
228
+ * Returns the image in a buffer (String). Optionally accepts a file
229
+ * format argument to convert the image to the specified format (see
230
+ * ImageScience::ImageFormats). If +format+ is nil, buffer() uses the
231
+ * current file format of the image (the default). +flags+ is optional
232
+ * flags to send to the writer plugin (see ImageScience::LoadSaveFlags).
233
+ */
234
+ static VALUE buffer(int argc, VALUE *argv, VALUE self) {
235
+ VALUE str = Qnil;
236
+ int flags;
237
+ FIBITMAP *bitmap, *new_bitmap;
238
+ FIMEMORY *mem = NULL;
239
+ BYTE *mem_buffer = NULL;
240
+ DWORD size_in_bytes = 0;
241
+ char message[1024];
242
+ FREE_IMAGE_FORMAT fif;
243
+ BOOL result = 0;
244
+ VALUE fif_arg, flags_arg; /* optional argument */
245
+
246
+ /* parse optional arguments: fif and flags */
247
+ rb_scan_args(argc, argv, "02", &fif_arg, &flags_arg);
248
+ if(NIL_P(fif_arg)) fif_arg = rb_iv_get(self, "@file_type");
249
+ fif = NIL_P(fif_arg) ? FIF_UNKNOWN : FIX2INT(fif_arg);
250
+ flags = NIL_P(flags_arg) ? 0 : FIX2INT(flags_arg);
251
+
252
+ if ((fif == FIF_UNKNOWN) || !FreeImage_FIFSupportsWriting(fif)) {
253
+ snprintf(message, 1023, "Unknown file format: %d", fif);
254
+ rb_raise(rb_eTypeError, message);
255
+ }
256
+
257
+ GET_BITMAP(bitmap);
258
+
259
+ // create a memory stream and save to it
260
+ new_bitmap = _prepare_bitmap_for_save(bitmap, fif);
261
+ mem = FreeImage_OpenMemory(0,0);
262
+ result = FreeImage_SaveToMemory(fif, new_bitmap ? new_bitmap : bitmap, mem, flags);
263
+
264
+ if(result) {
265
+ // get the buffer from the memory stream
266
+ FreeImage_AcquireMemory(mem, &mem_buffer, &size_in_bytes);
267
+
268
+ // convert to ruby string
269
+ str = rb_str_new((char *) mem_buffer, size_in_bytes);
270
+ }
271
+
272
+ // clean up
273
+ if(new_bitmap) FreeImage_Unload(new_bitmap);
274
+ FreeImage_CloseMemory(mem);
275
+
276
+ // yield the string, or return it
277
+ if (rb_block_given_p()) rb_yield(str);
278
+
279
+ return str;
280
+ }
281
+
282
+ /*
283
+ * call-seq:
284
+ * crop(left, top, right, bottom)
285
+ * with_crop(left, top, right, bottom) { |img| ... }
286
+ *
287
+ * Crops an image to +left+, +top+, +right+, and +bottom+.
288
+ * If a block is given, yields the new image, else returns
289
+ * true on success.
290
+ */
291
+ static VALUE with_crop(VALUE self, VALUE lv, VALUE tv, VALUE rv, VALUE bv) {
292
+ FIBITMAP *copy, *bitmap;
293
+ VALUE result = Qnil;
294
+
295
+ GET_BITMAP(bitmap);
296
+
297
+ copy = FreeImage_Copy(bitmap, NUM2INT(lv), NUM2INT(tv),
298
+ NUM2INT(rv), NUM2INT(bv));
299
+ if (copy) {
300
+ copy_icc_profile(bitmap, copy, get_fif(self));
301
+ if(rb_block_given_p()) {
302
+ result = wrap_and_yield(copy, self, 0);
303
+ } else {
304
+ result = Qtrue;
305
+ FreeImage_Unload(bitmap);
306
+ DATA_PTR(self) = copy;
307
+ }
308
+ }
309
+ return result;
310
+ }
311
+
312
+ /*
313
+ * Returns the height of the image, in pixels.
314
+ */
315
+ static VALUE height(VALUE self) {
316
+ FIBITMAP *bitmap;
317
+ GET_BITMAP(bitmap);
318
+ return INT2FIX(FreeImage_GetHeight(bitmap));
319
+ }
320
+
321
+ /*
322
+ * Returns the width of the image, in pixels.
323
+ */
324
+ static VALUE width(VALUE self) {
325
+ FIBITMAP *bitmap;
326
+ GET_BITMAP(bitmap);
327
+ return INT2FIX(FreeImage_GetWidth(bitmap));
328
+ }
329
+
330
+ /*
331
+ * call-seq:
332
+ * get_pixel_color(x, y) -> [red, green, blue, alpha]
333
+ *
334
+ * Returns an array representing the color of the given pixel.
335
+ */
336
+ static VALUE get_pixel_color(VALUE self, VALUE xval, VALUE yval) {
337
+ FIBITMAP *bitmap;
338
+ RGBQUAD rgb;
339
+ RGBQUAD *pal;
340
+ BYTE rgb_index;
341
+ FREE_IMAGE_COLOR_TYPE ctype;
342
+ VALUE out_ary = rb_ary_new2(4);
343
+ int x = NUM2INT(xval);
344
+ int y = NUM2INT(yval);
345
+ int success = 0;
346
+
347
+ GET_BITMAP(bitmap);
348
+ ctype = FreeImage_GetColorType(bitmap);
349
+
350
+ y = FreeImage_GetHeight(bitmap) - y - 1; /* convert to normal coordinates */
351
+
352
+ if(ctype == FIC_PALETTE) {
353
+ if(FreeImage_GetPixelIndex(bitmap, x, y, &rgb_index)) {
354
+ pal = FreeImage_GetPalette(bitmap);
355
+ if(pal) {
356
+ rgb = pal[rgb_index];
357
+ success = 1;
358
+ }
359
+ }
360
+ } else {
361
+ memset(&rgb, 0, sizeof(RGBQUAD));
362
+ success = FreeImage_GetPixelColor(bitmap, x, y, &rgb);
363
+ }
364
+
365
+ if(success) {
366
+ rb_ary_store(out_ary, 0, INT2FIX(rgb.rgbRed));
367
+ rb_ary_store(out_ary, 1, INT2FIX(rgb.rgbGreen));
368
+ rb_ary_store(out_ary, 2, INT2FIX(rgb.rgbBlue));
369
+ rb_ary_store(out_ary, 3, INT2FIX(rgb.rgbReserved));
370
+ }
371
+
372
+ return out_ary;
373
+ }
374
+
375
+ /*
376
+ * call-seq:
377
+ * set_pixel_color(x, y, index)
378
+ * set_pixel_color(x, y, red, green, blue, alpha = NULL)
379
+ * set_pixel_color(x, y, [red, green, blue, alpha])
380
+ *
381
+ * Set the pixel color or index (for palettized images). Parameter
382
+ * +x+ is the pixel position in horizontal direction, and parameter
383
+ * +y+ is the pixel position in vertical direction. The method
384
+ * returns true on success, and returns false otherwise.
385
+ */
386
+
387
+ static VALUE set_pixel_color(int argc, VALUE *argv, VALUE self) {
388
+ FIBITMAP *bitmap;
389
+ VALUE x, y, set_value, value1;
390
+ int value_length = 0, yval;
391
+ FREE_IMAGE_COLOR_TYPE ctype;
392
+ RGBQUAD rgb;
393
+ BYTE index_value;
394
+ BOOL ret = Qfalse;
395
+
396
+ GET_BITMAP(bitmap);
397
+ ctype = FreeImage_GetColorType(bitmap);
398
+ yval = FreeImage_GetHeight(bitmap); /* to convert to normal coordinates */
399
+
400
+ if(ctype == FIC_PALETTE) {
401
+ /* palettized image */
402
+ rb_scan_args(argc, argv, "30", &x, &y, &set_value);
403
+ index_value = FIX2INT(set_value);
404
+ yval = yval - FIX2INT(y) - 1;
405
+
406
+ ret = FreeImage_SetPixelIndex(bitmap, FIX2INT(x), yval, &index_value);
407
+ }
408
+ else {
409
+ rb_scan_args(argc, argv, "2*", &x, &y, &set_value);
410
+
411
+ if(!NIL_P(set_value)) {
412
+ /* if first entry is an array, use that as the colour list */
413
+ value1 = rb_ary_entry(set_value, 0);
414
+ if(TYPE(value1) == T_ARRAY) set_value = value1;
415
+
416
+ value_length = RARRAY(set_value)->len;
417
+ }
418
+
419
+ if(value_length < 3 || value_length > 4) {
420
+ rb_raise(rb_eArgError, "wrong number of arguments");
421
+ }
422
+
423
+ rgb.rgbRed = FIX2INT(rb_ary_entry(set_value, 0));
424
+ rgb.rgbGreen = FIX2INT(rb_ary_entry(set_value, 1));
425
+ rgb.rgbBlue = FIX2INT(rb_ary_entry(set_value, 2));
426
+ rgb.rgbReserved = (value_length == 4) ? FIX2INT(rb_ary_entry(set_value, 3)) : 0;
427
+ yval = yval - FIX2INT(y) - 1;
428
+
429
+ ret = FreeImage_SetPixelColor(bitmap, FIX2INT(x), yval, &rgb);
430
+ }
431
+ return ret;
432
+ }
433
+
434
+ /*
435
+ * call-seq:
436
+ * resize(width, height, filter = FILTER_CATMULLROM)
437
+ * resize(width, height, filter = FILTER_CATMULLROM) { |img| ... }
438
+ *
439
+ * Resizes the image to +width+ and +height+. Optionally specify a filter to
440
+ * use with the filter argument (See FreeImage::ImageFilters).
441
+ * If a block is given, yields the new image, else returns
442
+ * true on success.
443
+ */
444
+ static VALUE resize(int argc, VALUE *argv, VALUE self) {
445
+ FIBITMAP *bitmap, *image;
446
+ VALUE width, height, filter;
447
+ FREE_IMAGE_FILTER fi_filter;
448
+ int w, h;
449
+
450
+ rb_scan_args(argc, argv, "21", &width, &height, &filter);
451
+ fi_filter = NIL_P(filter) ? FILTER_CATMULLROM : FIX2INT(filter);
452
+
453
+ w = NUM2INT(width);
454
+ h = NUM2INT(height);
455
+
456
+ if (w <= 0) rb_raise(rb_eArgError, "Width <= 0");
457
+ if (h <= 0) rb_raise(rb_eArgError, "Height <= 0");
458
+
459
+ GET_BITMAP(bitmap);
460
+ image = FreeImage_Rescale(bitmap, w, h, fi_filter);
461
+
462
+ if (image) {
463
+ copy_icc_profile(bitmap, image, get_fif(self));
464
+ if(rb_block_given_p()) {
465
+ return wrap_and_yield(image, self, 0);
466
+ } else {
467
+ FreeImage_Unload(bitmap);
468
+ DATA_PTR(self) = image;
469
+ }
470
+ }
471
+ return image ? Qtrue : Qfalse;
472
+ }
473
+
474
+ /*
475
+ * call-seq:
476
+ * save(path)
477
+ *
478
+ * Saves the image out to +path+. Changing the file extension will
479
+ * convert the file type to the appropriate format.
480
+ */
481
+ static VALUE save(VALUE self, VALUE filename) {
482
+ int flags;
483
+ char * output = RSTRING(filename)->ptr;
484
+ FIBITMAP *bitmap, *new_bitmap;
485
+ FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(output);
486
+ if (fif == FIF_UNKNOWN) fif = FIX2INT(rb_iv_get(self, "@file_type"));
487
+ if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsWriting(fif)) {
488
+ GET_BITMAP(bitmap);
489
+ flags = fif == FIF_JPEG ? JPEG_QUALITYSUPERB : 0;
490
+ BOOL result = 0;
491
+
492
+ new_bitmap = _prepare_bitmap_for_save(bitmap, fif);
493
+ result = FreeImage_Save(fif, new_bitmap ? new_bitmap : bitmap, output, flags);
494
+ if(new_bitmap) FreeImage_Unload(new_bitmap);
495
+
496
+ return result ? Qtrue : Qfalse;
497
+ }
498
+ rb_raise(rb_eTypeError, "Unknown file format");
499
+ }
500
+
501
+ /*
502
+ * Investigates the color type of the bitmap by reading the bitmap's
503
+ * pixel bits and analysing them. See ImageScience::ColorTypes for
504
+ * return values.
505
+ */
506
+ static VALUE colortype(VALUE self) {
507
+ FIBITMAP *bitmap;
508
+ GET_BITMAP(bitmap);
509
+
510
+ return INT2FIX(FreeImage_GetColorType(bitmap));
511
+ }
512
+
513
+ /*
514
+ * Returns the size of one pixel in the bitmap in bits. For example
515
+ * when each pixel takes 32-bits of space in the bitmap, this method
516
+ * returns 32. Possible bit depths are 1, 4, 8, 16, 24, 32 for
517
+ * standard bitmaps and 16-, 32-, 48-, 64-, 96- and 128-bit for non
518
+ * standard bitmaps.
519
+ */
520
+ static VALUE depth(VALUE self) {
521
+ FIBITMAP *bitmap;
522
+ GET_BITMAP(bitmap);
523
+
524
+ return INT2FIX(FreeImage_GetBPP(bitmap));
525
+ }
526
+
527
+ /*
528
+ * call-seq:
529
+ * adjust_gamma(gamma) -> boolean
530
+ *
531
+ * Performs gamma correction on a 8-, 24- or 32-bit image. The gamma
532
+ * parameter represents the gamma value to use (gamma > 0). A value of
533
+ * 1.0 leaves the image alone, less than one darkens it, and greater
534
+ * than one lightens it. The method returns true on success. It
535
+ * returns false when gamma is less than or equal to zero or when the
536
+ * bitdepth of the image cannot be handled.
537
+ */
538
+ static VALUE adjust_gamma(VALUE self, VALUE gamma) {
539
+ FIBITMAP *bitmap;
540
+ GET_BITMAP(bitmap);
541
+ return RB_BOOL(FreeImage_AdjustGamma(bitmap, NUM2DBL(gamma)));
542
+ }
543
+
544
+ /*
545
+ * call-seq:
546
+ * adjust_brightness(percentage) -> boolean
547
+ *
548
+ * Adjusts the brightness of a 8-, 24- or 32-bit image by a certain
549
+ * amount. This amount is given by the percentage parameter, where
550
+ * percentage is a value between [-100..100]. A value 0 means no
551
+ * change, less than 0 will make the image darker and greater than 0
552
+ * will make the image brighter. The method returns true on
553
+ * success, false otherwise (e.g. when the bitdepth of the image
554
+ * cannot be handled).
555
+ */
556
+ static VALUE adjust_brightness(VALUE self, VALUE percentage) {
557
+ FIBITMAP *bitmap;
558
+ GET_BITMAP(bitmap);
559
+ return RB_BOOL(FreeImage_AdjustBrightness(bitmap, NUM2DBL(percentage)));
560
+ }
561
+
562
+ /*
563
+ * call-seq:
564
+ * adjust_contrast(percentage) -> boolean
565
+ *
566
+ * Adjusts the contrast of a 8-, 24- or 32-bit image by a certain
567
+ * amount. This amount is given by the percentage parameter, where
568
+ * percentage is a value between [-100..100]. A value 0 means no change,
569
+ * less than 0 will decrease the contrast and greater than 0 will
570
+ * increase the contrast of the image. The method returns true on
571
+ * success, false otherwise (e.g. when the bitdepth of the image cannot
572
+ * be handled).
573
+ */
574
+ static VALUE adjust_contrast(VALUE self, VALUE percentage) {
575
+ FIBITMAP *bitmap;
576
+ GET_BITMAP(bitmap);
577
+ return RB_BOOL(FreeImage_AdjustContrast(bitmap, NUM2DBL(percentage)));
578
+ }
579
+
580
+ /* Inverts each pixel data. Returns true on success. */
581
+ static VALUE invert(VALUE self) {
582
+ FIBITMAP *bitmap;
583
+ GET_BITMAP(bitmap);
584
+ return RB_BOOL(FreeImage_Invert(bitmap));
585
+ }
586
+
587
+ /*
588
+ * call-seq:
589
+ * histogram(channel = FICC_RGB) -> array or nil
590
+ *
591
+ * Computes the image histogram. For 24-bit and 32-bit images,
592
+ * histogram can be computed from red, green, blue and black
593
+ * channels. For 8-bit images, histogram is computed from the black
594
+ * channel. Other bit depth is not supported.
595
+ * The method returns a 256 element array on success, nil otherwise.
596
+ * See ImageScience::ColorChannels for accepted channel values.
597
+ */
598
+ static VALUE histogram(int argc, VALUE *argv, VALUE self) {
599
+ FIBITMAP *bitmap;
600
+ DWORD histo[256];
601
+ VALUE channel; /* optional argument */
602
+ FREE_IMAGE_COLOR_CHANNEL fi_chan;
603
+ VALUE ret = Qnil;
604
+ int i;
605
+
606
+ rb_scan_args(argc, argv, "01", &channel); /* 1 optional argument */
607
+
608
+ fi_chan = NIL_P(channel) ? FICC_RGB : FIX2INT(channel);
609
+
610
+ GET_BITMAP(bitmap);
611
+ if(FreeImage_GetHistogram(bitmap, histo, fi_chan)) {
612
+ ret = rb_ary_new2(256);
613
+ for(i = 0; i < 256; i++) {
614
+ rb_ary_store(ret, i, INT2FIX(histo[i]));
615
+ }
616
+ }
617
+ return ret;
618
+ }
619
+
620
+ static VALUE _flip(VALUE self, BOOL (*flip_func)(FIBITMAP *)) {
621
+ FIBITMAP *bitmap, *new_bitmap;
622
+ BOOL result;
623
+
624
+ GET_BITMAP(bitmap);
625
+ if(rb_block_given_p()) {
626
+ new_bitmap = FreeImage_Clone(bitmap);
627
+ (*flip_func)(new_bitmap);
628
+ return wrap_and_yield(new_bitmap, self, 0);
629
+ } else {
630
+ result = (*flip_func)(bitmap);
631
+ return result ? Qtrue : Qfalse;
632
+ }
633
+ }
634
+
635
+ /* Flip the image horizontally along the vertical axis. */
636
+ static VALUE flip_horizontal(VALUE self) {
637
+ return _flip(self, FreeImage_FlipHorizontal);
638
+ }
639
+
640
+ /* Flip the image vertically along the horizontal axis. */
641
+ static VALUE flip_vertical(VALUE self) {
642
+ return _flip(self, FreeImage_FlipVertical);
643
+ }
644
+
645
+ /*
646
+ * call-seq:
647
+ * rotate(angle)
648
+ * rotate(angle, x_shift, y_shift, x_origin, y_origin, use_mask = true)
649
+ *
650
+ * Perform a rotation and / or translation of an image.
651
+ *
652
+ * The first form rotates an image by means of 3 shears. The angle of
653
+ * rotation is specified by the +angle+ parameter in degrees. Rotation
654
+ * occurs around the center of the image area. The image retains the
655
+ * size and aspect ratio of the source image.
656
+ *
657
+ * The second form uses a 3rd order (cubic) B-Spline. The rotated
658
+ * image will have the same width and height as the source image. The
659
+ * angle of rotation is specified by the +angle+ parameter in
660
+ * degrees. Horizontal and vertical image translations (in pixel
661
+ * units) are specified by the +x_shift+ and +y_shift+
662
+ * parameters. Rotation occurs around the center specified by
663
+ * +x_origin+ and +y_origin+, also given in pixel units. When
664
+ * +use_mask+ is set to TRUE, the irrelevant part of the image is set
665
+ * to a black color, otherwise, a mirroring technique is used to fill
666
+ * irrelevant pixels.
667
+ *
668
+ */
669
+ static VALUE rotate(int argc, VALUE *argv, VALUE self) {
670
+ FIBITMAP *bitmap;
671
+ FIBITMAP *new_bitmap = NULL;
672
+ VALUE angle, x_shift, y_shift, x_origin, y_origin, use_mask;
673
+ BOOL mask;
674
+ FREE_IMAGE_FORMAT fif;
675
+
676
+ rb_scan_args(argc, argv, "15", &angle, &x_shift, &y_shift,
677
+ &x_origin, &y_origin, &use_mask);
678
+
679
+ GET_BITMAP(bitmap);
680
+ fif = get_fif(self);
681
+
682
+ if(FreeImage_GetBPP(bitmap) == 16) {
683
+ /* can't rotate 16 bit images - convert to 24 bit */
684
+ new_bitmap = FreeImage_ConvertTo24Bits(bitmap);
685
+ copy_icc_profile(bitmap, new_bitmap, fif);
686
+ FreeImage_Unload(bitmap);
687
+ DATA_PTR(self) = bitmap = new_bitmap;
688
+ new_bitmap = NULL;
689
+ }
690
+
691
+ if (argc == 1) {
692
+ /* first form - use Rotate */
693
+ new_bitmap = ROTATE(bitmap, NUM2DBL(angle));
694
+ }
695
+ else if (argc == 5 || argc == 6) {
696
+ /* second form - use RotateEx */
697
+ mask = NIL_P(use_mask) ? 0 : FIX2INT(use_mask);
698
+ new_bitmap = FreeImage_RotateEx
699
+ (bitmap, NUM2DBL(angle), NUM2DBL(x_shift), NUM2DBL(y_shift),
700
+ NUM2DBL(x_origin), NUM2DBL(y_origin), mask);
701
+ }
702
+ else {
703
+ rb_raise(rb_eArgError, "wrong number of arguments");
704
+ }
705
+ if (!new_bitmap) return Qfalse;
706
+
707
+ copy_icc_profile(bitmap, new_bitmap, fif);
708
+
709
+ if (rb_block_given_p()) {
710
+ return wrap_and_yield(new_bitmap, self, 0);
711
+ } else {
712
+ FreeImage_Unload(bitmap);
713
+ DATA_PTR(self) = new_bitmap;
714
+ return Qtrue;
715
+ }
716
+ }
717
+
718
+ /*
719
+ * call-seq:
720
+ * ImageScience.new(filename, flags = 0)
721
+ * ImageScience.new(image_data, flags = 0)
722
+ *
723
+ * Returns a new ImageScience object. The optional +flags+ argument are
724
+ * flags to send to the loader plugin (see ImageScience::LoadSaveFlags).
725
+ * If flags are not specified, the JPEG_ACCURATE flag will be
726
+ * enabled by default for JPEG files.
727
+ */
728
+
729
+ static VALUE isc_init(int argc, VALUE *argv, VALUE self)
730
+ {
731
+ VALUE image, flags_arg;
732
+ struct stat buf;
733
+ int is_file;
734
+ char *img;
735
+
736
+ rb_scan_args(argc, argv, "11", &image, &flags_arg);
737
+ Check_Type(image, T_STRING);
738
+ img = RSTRING(image)->ptr;
739
+ is_file = !stat(img, &buf);
740
+
741
+ return isc_init2(self, image, flags_arg, is_file);
742
+ }
743
+
744
+ /* -- extension initialiser ---- */
745
+
746
+ void Init_image_science_ext(void)
747
+ {
748
+ isc = rb_define_class("ImageScience", rb_cObject);
749
+ rb_define_singleton_method(isc, "with_image", with_image, 1);
750
+ rb_define_singleton_method(isc, "with_image_from_memory",
751
+ with_image_from_memory, 1);
752
+ rb_define_singleton_method(isc, "get_version", get_version, 0);
753
+ rb_define_singleton_method(isc, "file_type", file_type, 1);
754
+
755
+ rb_define_alloc_func(isc, isc_alloc);
756
+
757
+ rb_define_method(isc, "initialize", isc_init, -1);
758
+ rb_define_method(isc, "width", width, 0);
759
+ rb_define_method(isc, "height", height, 0);
760
+ rb_define_method(isc, "resize", resize, -1);
761
+ rb_define_method(isc, "save", save, 1);
762
+ rb_define_method(isc, "with_crop", with_crop, 4);
763
+ rb_define_method(isc, "crop", with_crop, 4);
764
+ rb_define_method(isc, "get_pixel_color", get_pixel_color, 2);
765
+ rb_define_method(isc, "set_pixel_color", set_pixel_color, -1);
766
+ rb_define_method(isc, "colortype", colortype, 0);
767
+ rb_define_method(isc, "depth", depth, 0);
768
+ rb_define_method(isc, "adjust_gamma", adjust_gamma, 1);
769
+ rb_define_method(isc, "adjust_brightness", adjust_brightness, 1);
770
+ rb_define_method(isc, "adjust_contrast", adjust_contrast, 1);
771
+ rb_define_method(isc, "invert", invert, 0);
772
+ rb_define_method(isc, "histogram", histogram, -1); /* variable args */
773
+ rb_define_method(isc, "buffer", buffer, -1);
774
+ rb_define_method(isc, "flip_horizontal", flip_horizontal, 0);
775
+ rb_define_method(isc, "flip_vertical", flip_vertical, 0);
776
+ rb_define_method(isc, "rotate", rotate, -1);
777
+
778
+ /* FREE_IMAGE_TYPE constants */
779
+ isc_image_types = rb_define_module_under(isc, "ImageTypes");
780
+ /* expand FreeImage constants isc_image_types FIT */
781
+
782
+ /* FREE_IMAGE_COLOR_CHANNEL constants */
783
+ isc_color_chan = rb_define_module_under(isc, "ColorChannels");
784
+ /* expand FreeImage constants isc_color_chan FICC */
785
+
786
+ /* FREE_IMAGE_COLOR_TYPE constants */
787
+ isc_color_types = rb_define_module_under(isc, "ColorTypes");
788
+ /* expand FreeImage constants isc_color_types FIC */
789
+
790
+ /* FREE_IMAGE_FORMATS constants */
791
+ isc_image_formats = rb_define_module_under(isc, "ImageFormats");
792
+ /* expand FreeImage constants isc_image_formats FIF */
793
+
794
+ /* IMAGE_FILTER constants */
795
+ isc_image_filters = rb_define_module_under(isc, "ImageFilters");
796
+ /* expand FreeImage constants isc_image_filters FILTER */
797
+
798
+ /* load/save flag constants */
799
+ isc_ls_flags = rb_define_module_under(isc, "LoadSaveFlags");
800
+ /* expand FreeImage constants isc_ls_flags FLAG */
801
+
802
+ FreeImage_SetOutputMessage(FreeImageErrorHandler);
803
+ }