magickwand 0.1.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,2296 @@
1
+ /*
2
+ * wand.c
3
+ * $Id: wand.c 147 2009-05-31 22:01:22Z rmagick $
4
+ * Copyright (C) 2009 Timothy Paul Hunter
5
+ */
6
+
7
+ #include <math.h>
8
+ #include "magickwand.h"
9
+
10
+
11
+
12
+
13
+ static void wand_destroy(void *);
14
+ static VALUE wand_length(VALUE);
15
+ static VALUE wand_new(MagickWand *);
16
+ static VALUE wand_signature(VALUE);
17
+
18
+
19
+
20
+
21
+
22
+ static VALUE wand_allocate(VALUE klass)
23
+ {
24
+ Wand *wand;
25
+ return Data_Make_Struct(klass, Wand, NULL, wand_destroy, wand);
26
+ }
27
+
28
+
29
+
30
+
31
+ static void wand_destroy(void *p)
32
+ {
33
+ Wand *wand = (Wand *)p;
34
+
35
+ if (wand->magickwand)
36
+ {
37
+ wand->magickwand = DestroyMagickWand(wand->magickwand);
38
+ }
39
+ }
40
+
41
+
42
+
43
+
44
+ /*
45
+ * wand.add_canvas(width, height = width, background = "white")
46
+ * default height is same as width
47
+ */
48
+ static VALUE wand_add_canvas(int argc, VALUE *argv, VALUE obj)
49
+ {
50
+ Wand *wand;
51
+ PixelWand *pixelwand;
52
+ Image *new_image;
53
+ volatile VALUE width, height, background;
54
+ unsigned long columns, rows;
55
+ char *background_color = "white";
56
+
57
+ rb_check_frozen(obj);
58
+
59
+ switch (rb_scan_args(argc, argv, "12", &width, &height, &background))
60
+ {
61
+ case 3:
62
+ background_color = StringValuePtr(background);
63
+ // fall through
64
+ case 2:
65
+ rows = NUM2ULONG(height);
66
+ columns = NUM2ULONG(width);
67
+ break;
68
+ case 1:
69
+ rows = columns = NUM2ULONG(width);
70
+ break;
71
+ }
72
+
73
+ Data_Get_Struct(obj, Wand, wand);
74
+
75
+ pixelwand = NewPixelWand();
76
+ PixelSetColor(pixelwand, background_color);
77
+ mwr_check_pixelwand_error(pixelwand);
78
+
79
+ // New images are added at the end.
80
+ MagickSetLastIterator(wand->magickwand);
81
+ // The image background color is NOT taken from the pixelwand!
82
+ // This must be done separately.
83
+ if (MagickNewImage(wand->magickwand, columns, rows, pixelwand) != MagickFalse)
84
+ {
85
+ new_image = GetImageFromMagickWand(wand->magickwand);
86
+ mwr_get_pixelpacket_from_pixelwand(&new_image->background_color, pixelwand);
87
+ SetImageBackgroundColor(new_image);
88
+ }
89
+ DestroyPixelWand(pixelwand);
90
+ mwr_check_magickwand_error(wand->magickwand);
91
+
92
+ return obj;
93
+ }
94
+
95
+
96
+
97
+
98
+ /*
99
+ * wand.animate(:delay => ticks, :iterations => 0)
100
+ * wand.animate(:delay => [ticks, ticks-per-second])
101
+ */
102
+ static VALUE wand_animate(int argc, VALUE *argv, VALUE obj)
103
+ {
104
+ Wand *wand;
105
+ volatile VALUE v;
106
+ VALUE options;
107
+ unsigned long delay = ULONG_MAX, iterations = ULONG_MAX;
108
+ long ticks_per_second = LONG_MAX;
109
+ int xyzzy = 1;
110
+
111
+ Data_Get_Struct(obj, Wand, wand);
112
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
113
+ {
114
+ return obj;
115
+ }
116
+
117
+ (void) rb_scan_args(argc, argv, "01", &options);
118
+ if (options != Qnil)
119
+ {
120
+ v = rb_hash_aref(options, ID2SYM(rb_intern("delay")));
121
+ if (v != Qnil)
122
+ {
123
+ if (TYPE(v) == T_ARRAY)
124
+ {
125
+ delay = NUM2ULONG(rb_ary_entry(v, 0));
126
+ // For simplicity, allow the array to have only 1 entry
127
+ if (NUM2INT(rb_funcall(v, rb_intern("length"), 0)) > 1)
128
+ {
129
+ ticks_per_second = NUM2ULONG(rb_ary_entry(v, 1));
130
+ }
131
+ }
132
+ else
133
+ {
134
+ delay = NUM2ULONG(v);
135
+ }
136
+ }
137
+ v = rb_hash_aref(options, ID2SYM(rb_intern("iterations")));
138
+ if (v != Qnil)
139
+ {
140
+ iterations = NUM2ULONG(v);
141
+ }
142
+ v = rb_hash_aref(options, ID2SYM(rb_intern("xyzzy")));
143
+ if (v != Qnil)
144
+ {
145
+ xyzzy = RTEST(v);
146
+ }
147
+ }
148
+
149
+ if (delay != ULONG_MAX || iterations != ULONG_MAX)
150
+ {
151
+ MagickResetIterator(wand->magickwand);
152
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
153
+ {
154
+ if (iterations != ULONG_MAX)
155
+ {
156
+ MagickSetImageIterations(wand->magickwand, iterations);
157
+ mwr_check_magickwand_error(wand->magickwand);
158
+ }
159
+ if (delay != ULONG_MAX)
160
+ {
161
+ MagickSetImageDelay(wand->magickwand, delay);
162
+ mwr_check_magickwand_error(wand->magickwand);
163
+ }
164
+ if (ticks_per_second != LONG_MAX)
165
+ {
166
+ MagickSetImageTicksPerSecond(wand->magickwand, ticks_per_second);
167
+ mwr_check_magickwand_error(wand->magickwand);
168
+ }
169
+ }
170
+ }
171
+
172
+ // :xyzzy => false suppresses the actual animation (for testing)
173
+ if (xyzzy)
174
+ {
175
+ MagickAnimateImages(wand->magickwand, NULL);
176
+ mwr_check_magickwand_error(wand->magickwand);
177
+ }
178
+
179
+ return obj;
180
+ }
181
+
182
+
183
+
184
+
185
+ static VALUE subsequence(VALUE obj, long offset, long length)
186
+ {
187
+ Wand *wand;
188
+ MagickWand *new_magickwand = NULL;
189
+ MagickWand *temp;
190
+ long nimages, n;
191
+
192
+ Data_Get_Struct(obj, Wand, wand);
193
+ nimages = (long) MagickGetNumberImages(wand->magickwand);
194
+
195
+ if (length < 0 || offset >= nimages)
196
+ {
197
+ goto leave; // return a new, empty Wand object
198
+ }
199
+ if (offset < 0)
200
+ {
201
+ if (offset + nimages < 0)
202
+ {
203
+ goto leave; // return a new, empty Wand object
204
+ }
205
+ offset += nimages;
206
+ }
207
+ if (offset + length >= nimages)
208
+ {
209
+ length = nimages - offset;
210
+ }
211
+ if (length < 0)
212
+ {
213
+ goto leave; // return a new, empty Wand object
214
+ }
215
+
216
+ /*
217
+ * Add the specified images from the wand to the new wand.
218
+ */
219
+
220
+ MagickSetIteratorIndex(wand->magickwand, offset);
221
+ mwr_check_magickwand_error(wand->magickwand);
222
+ new_magickwand = MagickGetImage(wand->magickwand); // get image at 'offset'
223
+ mwr_check_magickwand_error(wand->magickwand);
224
+
225
+ for (n = 1; n < length && offset + n < nimages; n++)
226
+ {
227
+ MagickNextImage(wand->magickwand);
228
+ mwr_check_magickwand_error(wand->magickwand);
229
+ /*
230
+ * Tim, remember that temp is allocated from Ruby memory, so
231
+ * MagickGetImage either succeeds or Ruby raises an exception.
232
+ */
233
+ MagickSetLastIterator(new_magickwand);
234
+ temp = MagickGetImage(wand->magickwand);
235
+ MagickAddImage(new_magickwand, temp);
236
+ /*
237
+ * Destroy the temp magickwand before possibly raising an exception.
238
+ */
239
+ temp = DestroyMagickWand(temp);
240
+ mwr_check_magickwand_error(new_magickwand);
241
+ }
242
+
243
+ leave:
244
+ return wand_new(new_magickwand);
245
+ }
246
+
247
+
248
+
249
+
250
+ /*
251
+ * wand.annotate(text, :x=>0.0, :y=>0.0, :angle=>0.0 [, drawing_wand options])
252
+ */
253
+ static VALUE wand_annotate(int argc, VALUE *argv, VALUE obj)
254
+ {
255
+ Wand *wand;
256
+ GC *gc;
257
+ double x, y, angle;
258
+ volatile VALUE graphics_context;
259
+ VALUE v, text, options;
260
+ char *text_string;
261
+
262
+ rb_check_frozen(obj);
263
+
264
+ (void)rb_scan_args(argc, argv, "11", &text, &options);
265
+ text_string = StringValuePtr(text);
266
+ if (RSTRING_LEN(text) == 0)
267
+ {
268
+ rb_raise(rb_eArgError, "no text specified");
269
+ }
270
+ x = mwr_get_option(options, "x", &v) ? NUM2DBL(v) : 0.0;
271
+ y = mwr_get_option(options, "y", &v) ? NUM2DBL(v) : 0.0;
272
+ angle = mwr_get_option(options, "angle", &v) ? NUM2DBL(v) : 0.0;
273
+
274
+ graphics_context = mwr_gc_new();
275
+ Data_Get_Struct(graphics_context, GC, gc);
276
+
277
+ // Override default NorthWest gravity. It's useless.
278
+ DrawSetGravity(gc->drawingwand, CenterGravity);
279
+ mwr_process_options(graphics_context, options);
280
+
281
+ Data_Get_Struct(obj, Wand, wand);
282
+
283
+ MagickResetIterator(wand->magickwand);
284
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
285
+ {
286
+ MagickAnnotateImage(wand->magickwand, gc->drawingwand, x, y, angle, text_string);
287
+ }
288
+
289
+ return obj;
290
+ }
291
+
292
+
293
+
294
+
295
+ /*
296
+ * wand[fixnum]
297
+ * wand[fixnum, fixnum]
298
+ * wand[range]
299
+ *
300
+ * All return a new wand. Raises RangeError if subscript is out-of-range
301
+ */
302
+ static VALUE wand_aref(int argc, VALUE *argv, VALUE obj)
303
+ {
304
+ long nimages, begin, length;
305
+ volatile VALUE range_code;
306
+
307
+ if (argc == 2)
308
+ {
309
+ return subsequence(obj, FIX2LONG(argv[0]), FIX2LONG(argv[1]));
310
+ }
311
+ else if (TYPE(argv[0]) == T_FIXNUM)
312
+ {
313
+ return subsequence(obj, FIX2LONG(argv[0]), 1L);
314
+ }
315
+ else
316
+ {
317
+ nimages = FIX2LONG(wand_length(obj));
318
+
319
+ // Returns Qfalse if argv[0] isn't a Range object.
320
+ // Returns Qnil if out-of-range
321
+ range_code = rb_range_beg_len(argv[0], &begin, &length, nimages, 0);
322
+ if (range_code == Qnil)
323
+ {
324
+ return wand_new(NULL);
325
+ }
326
+ else if (range_code == Qfalse)
327
+ {
328
+ rb_raise(rb_eTypeError, "expecting Fixnum or Range, got %s", rb_obj_classname(argv[0]));
329
+ }
330
+
331
+ return subsequence(obj, begin, length);
332
+ }
333
+ }
334
+
335
+
336
+
337
+
338
+ /*
339
+ * Replace 'length' images starting at offset 'begin' with the images in obj2
340
+ */
341
+ static VALUE splice(VALUE obj, long begin, long length, VALUE obj2)
342
+ {
343
+ Wand *wand, *wand2;
344
+ long n, nimages;
345
+
346
+ if (length < 0)
347
+ {
348
+ rb_raise(rb_eIndexError, "negative length %ld", length);
349
+ }
350
+
351
+ Data_Get_Struct(obj, Wand, wand);
352
+ nimages = (long) MagickGetNumberImages(wand->magickwand);
353
+ if (nimages < begin || (begin < 0 && -begin > nimages))
354
+ {
355
+ rb_raise(rb_eIndexError, "index %ld out of wand", begin);
356
+ }
357
+
358
+ if (begin < 0)
359
+ {
360
+ begin += nimages;
361
+ }
362
+ if (nimages < length || nimages < (begin + length))
363
+ {
364
+ length = nimages - begin;
365
+ }
366
+ if (length > nimages)
367
+ {
368
+ rb_raise(rb_eIndexError, "length %ld exceeds wand length", length);
369
+ }
370
+
371
+ Data_Get_Struct(obj2, Wand, wand2);
372
+
373
+ // delete length images starting at begin
374
+ MagickSetIteratorIndex(wand->magickwand, begin);
375
+ mwr_check_magickwand_error(wand->magickwand);
376
+ for (n = 0; n < length; n++)
377
+ {
378
+ MagickRemoveImage(wand->magickwand);
379
+ mwr_check_magickwand_error(wand->magickwand);
380
+ }
381
+
382
+ // If there are no images in the rhs wand, we're done.
383
+ // Otherwise position the lhs to just prior the first image to be replaced
384
+ // because MagickAddImage adds images *after* the current image.
385
+ if (MagickGetNumberImages(wand2->magickwand) > 0UL)
386
+ {
387
+ if (begin == 0L)
388
+ {
389
+ MagickResetIterator(wand->magickwand);
390
+ }
391
+ else
392
+ {
393
+ MagickSetIteratorIndex(wand->magickwand, begin-1L);
394
+ }
395
+ mwr_check_magickwand_error(wand->magickwand);
396
+
397
+ MagickResetIterator(wand2->magickwand);
398
+ MagickAddImage(wand->magickwand, wand2->magickwand);
399
+ mwr_check_magickwand_error(wand->magickwand);
400
+ }
401
+
402
+
403
+ return obj;
404
+ }
405
+
406
+
407
+
408
+
409
+ /*
410
+ * wand[fixnum] = wand
411
+ * wand[fixnum, fixnum] = wand
412
+ * wand[range] = wand
413
+ */
414
+ static VALUE wand_aset(int argc, VALUE *argv, VALUE obj)
415
+ {
416
+ Wand *wand;
417
+ volatile VALUE range_code;
418
+ long nimages, begin, length;
419
+
420
+ rb_check_frozen(obj);
421
+
422
+ if (argc == 3)
423
+ {
424
+ return splice(obj, FIX2LONG(argv[0]), FIX2LONG(argv[1]), argv[2]);
425
+ }
426
+ else if (argc == 2)
427
+ {
428
+ if (rb_obj_is_kind_of(argv[0], rb_cRange))
429
+ {
430
+ Data_Get_Struct(obj, Wand, wand);
431
+ nimages = (long) MagickGetNumberImages(wand->magickwand);
432
+ range_code = rb_range_beg_len(argv[0], &begin, &length, nimages, 2);
433
+ return splice(obj, begin, length, argv[1]);
434
+ }
435
+ else
436
+ {
437
+ return splice(obj, FIX2LONG(argv[0]), 1UL, argv[1]);
438
+ }
439
+ }
440
+ else
441
+ {
442
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
443
+ }
444
+
445
+ return (VALUE)0; // not reachable
446
+ }
447
+
448
+
449
+
450
+
451
+ /*
452
+ * returns the background color property for the 0th image
453
+ */
454
+ static VALUE wand_background(VALUE obj)
455
+ {
456
+ Wand *wand;
457
+ Image *image;
458
+
459
+ if (FIX2LONG(wand_length(obj)) == 0)
460
+ {
461
+ return Qnil;
462
+ }
463
+
464
+ Data_Get_Struct(obj, Wand, wand);
465
+ MagickResetIterator(wand->magickwand);
466
+ image = GetImageFromMagickWand(wand->magickwand);
467
+ mwr_check_magickwand_error(wand->magickwand);
468
+ return mwr_pixelpacket_to_hex(image, &image->background_color);
469
+ }
470
+
471
+
472
+
473
+
474
+ static VALUE convolve(int argc, VALUE *argv, VALUE obj,
475
+ MagickBooleanType (*convolve)(MagickWand *, const ChannelType, const double, const double))
476
+ {
477
+ Wand *wand;
478
+ VALUE r, s, v, options;
479
+ double radius, sigma;
480
+ ChannelType channels;
481
+
482
+ rb_check_frozen(obj);
483
+
484
+ (void) rb_scan_args(argc, argv, "21", &r, &s, &options);
485
+ radius = NUM2DBL(r);
486
+ sigma = NUM2DBL(s);
487
+ (void) mwr_get_option(options, "channel", &v);
488
+ channels = mwr_string_to_channeltype(v, DefaultChannels, RaiseUndefinedOption);
489
+
490
+ Data_Get_Struct(obj, Wand, wand);
491
+ MagickResetIterator(wand->magickwand);
492
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
493
+ {
494
+ (*convolve)(wand->magickwand, channels, radius, sigma);
495
+ mwr_check_magickwand_error(wand->magickwand);
496
+ }
497
+
498
+ return obj;
499
+ }
500
+
501
+
502
+
503
+
504
+ /*
505
+ * wand.blur(radius, sigma, :channel=>"default")
506
+ */
507
+ static VALUE wand_blur(int argc, VALUE *argv, VALUE obj)
508
+ {
509
+ return convolve(argc, argv, obj, MagickBlurImageChannel);
510
+ }
511
+
512
+
513
+
514
+
515
+ /*
516
+ * wand.border(color, width=1, height=width)
517
+ */
518
+ static VALUE wand_border(int argc, VALUE *argv, VALUE obj)
519
+ {
520
+ Wand *wand;
521
+ PixelWand *pixelwand;
522
+ VALUE color, wt, ht;
523
+ char *border_color;
524
+ unsigned long width, height;
525
+
526
+ rb_check_frozen(obj);
527
+ (void) rb_scan_args(argc, argv, "12", &color, &wt, &ht);
528
+ border_color = StringValuePtr(color);
529
+ width = wt != Qnil ? NUM2ULONG(wt) : 1UL;
530
+ height = ht != Qnil ? NUM2ULONG(ht) : width;
531
+
532
+ pixelwand = NewPixelWand();
533
+ PixelSetColor(pixelwand, border_color);
534
+ mwr_check_pixelwand_error(pixelwand);
535
+
536
+ Data_Get_Struct(obj, Wand, wand);
537
+
538
+ MagickResetIterator(wand->magickwand);
539
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
540
+ {
541
+ MagickBorderImage(wand->magickwand, pixelwand, width, height);
542
+ mwr_check_magickwand_error(wand->magickwand);
543
+ }
544
+
545
+ DestroyPixelWand(pixelwand);
546
+ return obj;
547
+ }
548
+
549
+
550
+
551
+
552
+ /*
553
+ * wanda <=> wandb
554
+ * Comparison is similar to how arrays are compared.
555
+ * Each image in each wand is compared by their signatures.
556
+ * If all images are equal then the longer wand is > than the shorter wand.
557
+ */
558
+ static VALUE wand_cmp(VALUE obj, VALUE other)
559
+ {
560
+ Wand *wand, *other_wand;
561
+ int cmp;
562
+ char *signature, *other_signature;
563
+
564
+ if (obj == other)
565
+ {
566
+ return INT2FIX(0);
567
+ }
568
+
569
+ Data_Get_Struct(obj, Wand, wand);
570
+ Data_Get_Struct(other, Wand, other_wand);
571
+
572
+ MagickResetIterator(wand->magickwand);
573
+ MagickResetIterator(other_wand->magickwand);
574
+
575
+ cmp = 0;
576
+ while (cmp == 0 && MagickNextImage(wand->magickwand) && MagickNextImage(other_wand->magickwand))
577
+ {
578
+ signature = MagickGetImageSignature(wand->magickwand);
579
+ other_signature = MagickGetImageSignature(other_wand->magickwand);
580
+ cmp = strcmp(signature, other_signature);
581
+ RelinquishMagickMemory(signature);
582
+ RelinquishMagickMemory(other_signature);
583
+ }
584
+
585
+ if (cmp != 0)
586
+ {
587
+ return INT2FIX(cmp);
588
+ }
589
+ if (MagickHasNextImage(wand->magickwand))
590
+ {
591
+ return INT2FIX(1);
592
+ }
593
+ if (MagickHasNextImage(other_wand->magickwand))
594
+ {
595
+ return INT2FIX(-1);
596
+ }
597
+
598
+ return INT2FIX(0);
599
+ }
600
+
601
+
602
+
603
+
604
+ /*
605
+ * colors = wand.colors
606
+ */
607
+ static VALUE wand_colors(VALUE obj)
608
+ {
609
+ Wand *wand;
610
+ unsigned long colors;
611
+
612
+ Data_Get_Struct(obj, Wand, wand);
613
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
614
+ {
615
+ return Qnil;
616
+ }
617
+
618
+ MagickResetIterator(wand->magickwand);
619
+ colors = MagickGetImageColors(wand->magickwand);
620
+ mwr_check_magickwand_error(wand->magickwand);
621
+ return ULONG2NUM(colors);
622
+ }
623
+
624
+
625
+
626
+
627
+ /*
628
+ * string = img.comment
629
+ * Gets the comment from the first image
630
+ * There is no official "MagickGetComment" API for this operation.
631
+ */
632
+ static VALUE wand_comment(VALUE obj)
633
+ {
634
+ Image *magick_image;
635
+ Wand *wand;
636
+ const ImageAttribute *comment;
637
+
638
+ Data_Get_Struct(obj, Wand, wand);
639
+ MagickSetFirstIterator(wand->magickwand);
640
+ magick_image = GetImageFromMagickWand(wand->magickwand);
641
+ mwr_check_magickwand_error(wand->magickwand);
642
+ if (magick_image)
643
+ {
644
+ comment = GetImageAttribute(magick_image, "comment");
645
+ if (comment && comment->value)
646
+ {
647
+ return rb_str_new2(comment->value);
648
+ }
649
+ }
650
+
651
+ return Qnil;
652
+ }
653
+
654
+
655
+
656
+
657
+ /*
658
+ * img.comment = string # set comment to string
659
+ * img.comment = nil # removes comment
660
+ */
661
+ static VALUE wand_comment_set(VALUE obj, VALUE comment)
662
+ {
663
+ const char *comment_str = NULL;
664
+ Wand *wand;
665
+
666
+ rb_check_frozen(obj);
667
+
668
+ Data_Get_Struct(obj, Wand, wand);
669
+ if (comment != Qnil)
670
+ {
671
+ comment_str = StringValuePtr(comment);
672
+ }
673
+
674
+ MagickResetIterator(wand->magickwand);
675
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
676
+ {
677
+ (void) MagickCommentImage(wand->magickwand, comment_str);
678
+ mwr_check_magickwand_error(wand->magickwand);
679
+ }
680
+
681
+ return comment;
682
+ }
683
+
684
+
685
+
686
+
687
+ /*
688
+ * diff, distortion = wand.compare(wand2[, :metric=>"ae", :channel=>"default", :fuzz=>"0%")
689
+ * Compares the first image in wand to the first image in wand2.
690
+ * 'wand' is the reconstructed image, 'wand2' is the reference image.
691
+ * Returns a difference image and distortion metric.
692
+ */
693
+ static VALUE wand_compare(int argc, VALUE *argv, VALUE obj)
694
+ {
695
+ Wand *wand;
696
+ Wand *wand2;
697
+ MagickWand *result;
698
+ VALUE other, options, v;
699
+ MetricType metric;
700
+ ChannelType channels;
701
+ double distortion = 0.0;
702
+ double fuzz = 0.0;
703
+
704
+ (void) rb_scan_args(argc, argv, "11", &other, &options);
705
+ (void) mwr_get_option(options, "metric", &v);
706
+ metric = mwr_string_to_metrictype(v, AbsoluteErrorMetric, RaiseUndefinedOption);
707
+ (void) mwr_get_option(options, "channel", &v);
708
+ channels = mwr_string_to_channeltype(v, DefaultChannels, RaiseUndefinedOption);
709
+
710
+ Data_Get_Struct(obj, Wand, wand);
711
+ Data_Get_Struct(other, Wand, wand2);
712
+
713
+ MagickResetIterator(wand->magickwand);
714
+ MagickResetIterator(wand2->magickwand);
715
+
716
+ (void) mwr_get_option(options, "fuzz", &v);
717
+ #if defined(HAVE_MAGICKSETIMAGEFUZZ)
718
+ if (v != Qnil)
719
+ {
720
+ if (rb_obj_is_kind_of(v, rb_cNumeric))
721
+ {
722
+ fuzz = NUM2DBL(v);
723
+ }
724
+ else
725
+ {
726
+ v = rb_String(v);
727
+ // StringToDouble is defined in magick/string.c
728
+ fuzz = StringToDouble(StringValuePtr(v), QuantumRange+1.0);
729
+ }
730
+ }
731
+
732
+ MagickSetImageFuzz(wand->magickwand, fuzz);
733
+ #else
734
+ if (v != Qnil)
735
+ {
736
+ rb_warning("MagickWand: The :fuzz option is not supported with this release of ImageMagick.");
737
+ fuzz = fuzz;
738
+ }
739
+ #endif
740
+ result = MagickCompareImageChannels(wand->magickwand, wand2->magickwand, channels, metric, &distortion);
741
+
742
+ mwr_check_magickwand_error(wand->magickwand);
743
+ mwr_check_magickwand_error(wand2->magickwand);
744
+
745
+ return rb_ary_new3(2, wand_new(result), rb_float_new(distortion));
746
+ }
747
+
748
+
749
+
750
+
751
+ /*
752
+ * wand.composite(composite_wand, :compose=>"over", :x=>0, :y=>0, :gravity=>"center")
753
+ *
754
+ * Composites each image in composite_wand over the image in the corresponding position
755
+ * in wand. If the composite_wand has fewer images than wand, then its images
756
+ * are reused.
757
+ *
758
+ * The position of the upper-left corner of the composite image is x,y, as adjusted
759
+ * by gravity, relative to the current image and current composite image.
760
+ */
761
+ static VALUE wand_composite(int argc, VALUE *argv, VALUE obj)
762
+ {
763
+ volatile VALUE obj2;
764
+ VALUE v, options;
765
+ Wand *wand, *composite_wand;
766
+ CompositeOperator compose;
767
+ GravityType gravity;
768
+ unsigned long columns, rows;
769
+ long x, y;
770
+ RectangleInfo rect;
771
+
772
+ rb_check_frozen(obj);
773
+
774
+ (void) rb_scan_args(argc, argv, "11", &obj2, &options);
775
+
776
+ Data_Get_Struct(obj, Wand, wand);
777
+ Data_Get_Struct(obj2, Wand, composite_wand);
778
+ if (MagickGetNumberImages(wand->magickwand) == 0UL || MagickGetNumberImages(composite_wand->magickwand) == 0)
779
+ {
780
+ return obj;
781
+ }
782
+
783
+ memset(&rect, 0, sizeof(rect));
784
+
785
+ (void) mwr_get_option(options, "compose", &v);
786
+ compose = mwr_string_to_composetype(v, OverCompositeOp, RaiseUndefinedOption);
787
+ (void) mwr_get_option(options, "gravity", &v);
788
+ gravity = mwr_string_to_gravitytype(v, CenterGravity, RaiseUndefinedOption);
789
+ x = mwr_get_option(options, "x", &v) ? FIX2LONG(v) : 0L;
790
+ y = mwr_get_option(options, "y", &v) ? FIX2LONG(v) : 0L;
791
+
792
+ MagickResetIterator(wand->magickwand);
793
+ MagickResetIterator(composite_wand->magickwand);
794
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
795
+ {
796
+ if (MagickNextImage(composite_wand->magickwand) == MagickFalse)
797
+ {
798
+ MagickResetIterator(composite_wand->magickwand);
799
+ MagickNextImage(composite_wand->magickwand);
800
+ }
801
+ // Position is based on the size of the current image
802
+ columns = MagickGetImageWidth(wand->magickwand);
803
+ rows = MagickGetImageHeight(wand->magickwand);
804
+ rect.width = MagickGetImageWidth(composite_wand->magickwand);
805
+ rect.height = MagickGetImageHeight(composite_wand->magickwand);
806
+ rect.x = x;
807
+ rect.y = y;
808
+ (void) GravityAdjustGeometry(columns, rows, gravity, &rect);
809
+ (void) MagickCompositeImage(wand->magickwand, composite_wand->magickwand, compose, rect.x, rect.y);
810
+ mwr_check_magickwand_error(wand->magickwand);
811
+ }
812
+
813
+ return obj;
814
+ }
815
+
816
+
817
+
818
+
819
+ /*
820
+ * wand1 << wand2
821
+ * concats images in wand2 to wand1
822
+ */
823
+ static VALUE wand_concat(VALUE obj, VALUE obj2)
824
+ {
825
+ Wand *wand, *wand2;
826
+
827
+ rb_check_frozen(obj);
828
+
829
+ Data_Get_Struct(obj, Wand, wand);
830
+ Data_Get_Struct(obj2, Wand, wand2);
831
+
832
+ MagickSetLastIterator(wand->magickwand);
833
+ MagickAddImage(wand->magickwand, wand2->magickwand);
834
+ mwr_check_magickwand_error(wand->magickwand);
835
+ return obj;
836
+ }
837
+
838
+
839
+
840
+
841
+ /*
842
+ * wand.crop(width, height=width, :x=>x, :y=y, :gravity=gravity, :repage=boolean)
843
+ * x and y default to 0
844
+ * gravity defaults to NorthWestGravity
845
+ * repage defaults to true
846
+ */
847
+ static VALUE wand_crop(int argc, VALUE *argv, VALUE obj)
848
+ {
849
+ Wand *wand;
850
+ Image *image;
851
+ volatile VALUE width, height, options;
852
+ VALUE v;
853
+ unsigned long columns, rows;
854
+ long x, y;
855
+ GravityType gravity;
856
+ GravityType keep_gravity;
857
+ int repage;
858
+ RectangleInfo rect;
859
+ char geometry[200];
860
+ ExceptionInfo exception;
861
+
862
+ rb_check_frozen(obj);
863
+
864
+ (void) rb_scan_args(argc, argv, "12", &width, &height, &options);
865
+ columns = NUM2ULONG(width);
866
+ if (height != Qnil)
867
+ {
868
+ rows = NUM2ULONG(height);
869
+ }
870
+ else
871
+ {
872
+ rows = columns;
873
+ }
874
+ (void) mwr_get_option(options, "gravity", &v);
875
+ gravity = mwr_string_to_gravitytype(v, NorthWestGravity, RaiseUndefinedOption);
876
+ x = mwr_get_option(options, "x", &v) ? FIX2LONG(v) : 0L;
877
+ y = mwr_get_option(options, "y", &v) ? FIX2LONG(v) : 0L;
878
+ repage = mwr_get_option(options, "repage", &v) ? RTEST(v) : 1;
879
+
880
+ (void) sprintf(geometry, "%lux%lu%+ld%+ld", columns, rows, x, y);
881
+
882
+ Data_Get_Struct(obj, Wand, wand);
883
+ GetExceptionInfo(&exception);
884
+
885
+ MagickResetIterator(wand->magickwand);
886
+
887
+ // Crop each image individually. If they're different sizes then the
888
+ // crop rectangle could be different.
889
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
890
+ {
891
+ image = GetImageFromMagickWand(wand->magickwand);
892
+ mwr_check_magickwand_error(wand->magickwand);
893
+ keep_gravity = image->gravity;
894
+ image->gravity = gravity;
895
+ memset(&rect, 0, sizeof(rect));
896
+ (void) ParseGravityGeometry(image, geometry, &rect, &exception);
897
+ MagickCropImage(wand->magickwand, rect.width, rect.height, rect.x, rect.y);
898
+ image->gravity = keep_gravity;
899
+ mwr_check_magickwand_error(wand->magickwand);
900
+ if (repage)
901
+ {
902
+ MagickResetImagePage(wand->magickwand, "0x0+0+0");
903
+ mwr_check_magickwand_error(wand->magickwand);
904
+ }
905
+ }
906
+
907
+ DestroyExceptionInfo(&exception);
908
+
909
+ return obj;
910
+ }
911
+
912
+
913
+
914
+
915
+ /*
916
+ * depth = wand.depth
917
+ */
918
+ static VALUE wand_depth(VALUE obj)
919
+ {
920
+ Wand *wand;
921
+
922
+ Data_Get_Struct(obj, Wand, wand);
923
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
924
+ {
925
+ return Qnil;
926
+ }
927
+
928
+ MagickResetIterator(wand->magickwand);
929
+ return ULONG2NUM(MagickGetImageDepth(wand->magickwand));
930
+ }
931
+
932
+
933
+
934
+
935
+ /*
936
+ * [width, height] = wand.dimensions
937
+ * returns width, height of 1st/only image
938
+ */
939
+ static VALUE wand_dimensions(VALUE obj)
940
+ {
941
+ Wand *wand;
942
+ volatile VALUE dimensions;
943
+ unsigned long width, height;
944
+
945
+ Data_Get_Struct(obj, Wand, wand);
946
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
947
+ {
948
+ return Qnil;
949
+ }
950
+
951
+ MagickResetIterator(wand->magickwand);
952
+
953
+ dimensions = rb_ary_new2(2);
954
+
955
+ width = MagickGetImageWidth(wand->magickwand);
956
+ mwr_check_magickwand_error(wand->magickwand);
957
+ rb_ary_push(dimensions, ULONG2NUM(width));
958
+
959
+ height = MagickGetImageHeight(wand->magickwand);
960
+ mwr_check_magickwand_error(wand->magickwand);
961
+ rb_ary_push(dimensions, ULONG2NUM(height));
962
+
963
+ return dimensions;
964
+ }
965
+
966
+
967
+
968
+
969
+ static VALUE wand_display(VALUE obj)
970
+ {
971
+ Wand *wand;
972
+
973
+ Data_Get_Struct(obj, Wand, wand);
974
+ MagickDisplayImages(wand->magickwand, NULL);
975
+ mwr_check_magickwand_error(wand->magickwand);
976
+ return obj;
977
+ }
978
+
979
+
980
+
981
+
982
+ /*
983
+ * (>= 1.8.7) if no block return enumerator
984
+ */
985
+ static VALUE wand_each(VALUE obj)
986
+ {
987
+ Wand *wand;
988
+ unsigned long n, nimages;
989
+
990
+ #if defined(HAVE_RB_ENUMERATORIZE)
991
+ if (!rb_block_given_p())
992
+ {
993
+ return rb_enumeratorize(obj, ID2SYM(rb_frame_this_func()), 0, 0);
994
+ }
995
+ #endif
996
+
997
+ Data_Get_Struct(obj, Wand, wand);
998
+
999
+ // Can't use the typical Reset/GetNext loop here. The block
1000
+ // may change the wand's iterator.
1001
+ nimages = MagickGetNumberImages(wand->magickwand);
1002
+ for (n = 0; n < nimages; n++)
1003
+ {
1004
+ MagickSetIteratorIndex(wand->magickwand, n);
1005
+ rb_yield(wand_new(MagickGetImage(wand->magickwand)));
1006
+ }
1007
+
1008
+ return obj;
1009
+ }
1010
+
1011
+
1012
+
1013
+
1014
+ /*
1015
+ * returns true if wand.length == 0
1016
+ */
1017
+ static VALUE wand_empty(VALUE obj)
1018
+ {
1019
+ Wand *wand;
1020
+
1021
+ Data_Get_Struct(obj, Wand, wand);
1022
+ return MagickGetNumberImages(wand->magickwand) == 0UL ? Qtrue : Qfalse;
1023
+ }
1024
+
1025
+
1026
+
1027
+
1028
+ /*
1029
+ * wand.eql?(wand2)
1030
+ * returns true if wand <=> wand2 returns 0
1031
+ */
1032
+ static VALUE wand_eql(VALUE obj, VALUE obj2)
1033
+ {
1034
+ volatile VALUE eql;
1035
+
1036
+ eql = wand_cmp(obj, obj2);
1037
+ return FIX2INT(eql) == 0 ? Qtrue : Qfalse;
1038
+ }
1039
+
1040
+
1041
+
1042
+
1043
+ static size_t get_stg_type_size(StorageType stg_type)
1044
+ {
1045
+ size_t size;
1046
+
1047
+ switch (stg_type)
1048
+ {
1049
+ case CharPixel:
1050
+ size = 1;
1051
+ break;
1052
+ case ShortPixel:
1053
+ size = sizeof(short);
1054
+ break;
1055
+ case IntegerPixel:
1056
+ size = sizeof(int);
1057
+ break;
1058
+ case LongPixel:
1059
+ size = sizeof(long);
1060
+ break;
1061
+ case QuantumPixel:
1062
+ size = sizeof(Quantum);
1063
+ break;
1064
+ case FloatPixel:
1065
+ size = sizeof(float);
1066
+ break;
1067
+ case DoublePixel:
1068
+ size = sizeof(double);
1069
+ break;
1070
+ default:
1071
+ rb_bug("undefined storage type");
1072
+ break;
1073
+ }
1074
+
1075
+ return size;
1076
+ }
1077
+
1078
+
1079
+
1080
+ /*
1081
+ * str = wand.export_pixels(x, y, width, height, :storage_type=>"char", :map=>"rgb")
1082
+ */
1083
+ static VALUE wand_export_pixels(int argc, VALUE *argv, VALUE obj)
1084
+ {
1085
+ Wand *wand;
1086
+ VALUE x_off, y_off, width, height, options, v, pixels;
1087
+ long x, y, length;
1088
+ unsigned long columns, rows;
1089
+ StorageType stg_type;
1090
+ char *map;
1091
+ size_t stg_type_size;
1092
+
1093
+ (void) rb_scan_args(argc, argv, "41", &x_off, &y_off, &width, &height, &options);
1094
+ x = FIX2LONG(x_off);
1095
+ y = FIX2LONG(y_off);
1096
+ columns = NUM2ULONG(width);
1097
+ rows = NUM2ULONG(height);
1098
+ (void) mwr_get_option(options, "map", &v);
1099
+ map = v != Qnil ? StringValuePtr(v) : "RGB";
1100
+ (void) mwr_get_option(options, "storage_type", &v);
1101
+ stg_type = mwr_string_to_storagetype(v, CharPixel, RaiseUndefinedOption);
1102
+ stg_type_size = get_stg_type_size(stg_type);
1103
+
1104
+ if (strlen(map) == 0)
1105
+ {
1106
+ rb_raise(rb_eArgError, "no map specified");
1107
+ }
1108
+ length = (long) (columns * rows * strlen(map) * stg_type_size);
1109
+
1110
+
1111
+ // Allocate a string long enough to hold the exported pixel data.
1112
+ pixels = rb_str_new2("");
1113
+ (void) rb_str_resize(pixels, length);
1114
+
1115
+ Data_Get_Struct(obj, Wand, wand);
1116
+ MagickResetIterator(wand->magickwand);
1117
+ MagickExportImagePixels(wand->magickwand, x, y, columns, rows, map, stg_type, RSTRING_PTR(pixels));
1118
+ mwr_check_magickwand_error(wand->magickwand);
1119
+
1120
+ return pixels;
1121
+ }
1122
+
1123
+
1124
+
1125
+
1126
+ static VALUE wand_filename(VALUE obj)
1127
+ {
1128
+ Wand *wand;
1129
+ char *f;
1130
+ volatile VALUE filename;
1131
+
1132
+ Data_Get_Struct(obj, Wand, wand);
1133
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
1134
+ {
1135
+ return Qnil;
1136
+ }
1137
+
1138
+ MagickResetIterator(wand->magickwand);
1139
+ f = MagickGetImageFilename(wand->magickwand);
1140
+ mwr_check_magickwand_error(wand->magickwand);
1141
+ filename = rb_str_new2(f);
1142
+ MagickRelinquishMemory(f);
1143
+
1144
+ return filename;
1145
+
1146
+ }
1147
+
1148
+
1149
+
1150
+
1151
+ /*
1152
+ * format = wand.format
1153
+ */
1154
+ static VALUE wand_format(VALUE obj)
1155
+ {
1156
+ Wand *wand;
1157
+ char *f;
1158
+ volatile VALUE format;
1159
+
1160
+ Data_Get_Struct(obj, Wand, wand);
1161
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
1162
+ {
1163
+ return Qnil;
1164
+ }
1165
+
1166
+ MagickResetIterator(wand->magickwand);
1167
+ f = MagickGetImageFormat(wand->magickwand);
1168
+ mwr_check_magickwand_error(wand->magickwand);
1169
+ format = rb_str_new2(f);
1170
+ MagickRelinquishMemory(f);
1171
+
1172
+ return format;
1173
+ }
1174
+
1175
+
1176
+
1177
+
1178
+ /*
1179
+ * private method
1180
+ * [new_width, new_height] = wand.get_resize_geometry(geometry_string)
1181
+ */
1182
+ static VALUE wand_get_resize_geometry(VALUE obj, VALUE geometry)
1183
+ {
1184
+ Wand *wand;
1185
+ Image *image;
1186
+ volatile VALUE resize_geometry;
1187
+ RectangleInfo rect;
1188
+ unsigned int flags;
1189
+
1190
+ Data_Get_Struct(obj, Wand, wand);
1191
+ MagickResetIterator(wand->magickwand);
1192
+ image = GetImageFromMagickWand(wand->magickwand);
1193
+ mwr_check_magickwand_error(wand->magickwand);
1194
+
1195
+ memset(&rect, 0, sizeof(rect));
1196
+ SetGeometry(image, &rect);
1197
+ flags = ParseMetaGeometry(StringValuePtr(geometry), &rect.x, &rect.y, &rect.width, &rect.height);
1198
+ if (flags == NoValue)
1199
+ {
1200
+ rb_raise(rb_eArgError, "invalid geometry `%s'", StringValuePtr(geometry));
1201
+ }
1202
+
1203
+ resize_geometry = rb_ary_new2(2);
1204
+ rb_ary_store(resize_geometry, 0L, ULONG2NUM(rect.width));
1205
+ rb_ary_store(resize_geometry, 1L, ULONG2NUM(rect.height));
1206
+
1207
+ return resize_geometry;
1208
+ }
1209
+
1210
+
1211
+
1212
+
1213
+ /*
1214
+ * wand.import_pixels(x, y, columns, rows, pixels, :map=>"rgb", :storage_type=>"char")
1215
+ * imports into the first image in the wand
1216
+ */
1217
+ static VALUE wand_import_pixels(int argc, VALUE *argv, VALUE obj)
1218
+ {
1219
+ Wand *wand;
1220
+ VALUE columns, rows, pixels, options, x_off, y_off, v;
1221
+ unsigned long width, height, length;
1222
+ long x, y;
1223
+ void *data;
1224
+ StorageType stg_type;
1225
+ char *map;
1226
+ size_t stg_type_size;
1227
+
1228
+ rb_check_frozen(obj);
1229
+
1230
+ (void) rb_scan_args(argc, argv, "51", &x_off, &y_off, &columns, &rows, &pixels, &options);
1231
+ x = NUM2LONG(x_off);
1232
+ y = NUM2LONG(y_off);
1233
+ width = NUM2ULONG(columns);
1234
+ height = NUM2ULONG(rows);
1235
+ StringValue(pixels);
1236
+ data = (void *) RSTRING_PTR(pixels);
1237
+ length = (unsigned long) RSTRING_LEN(pixels);
1238
+
1239
+ (void) mwr_get_option(options, "map", &v);
1240
+ map = v != Qnil ? StringValuePtr(v) : "RGB";
1241
+ (void) mwr_get_option(options, "storage_type", &v);
1242
+ stg_type = mwr_string_to_storagetype(v, CharPixel, RaiseUndefinedOption);
1243
+ stg_type_size = get_stg_type_size(stg_type);
1244
+
1245
+ if (length < width*height*stg_type_size*strlen(map))
1246
+ {
1247
+ rb_raise(rb_eArgError, "pixel buffer too small (expecting %lu got %lu)",
1248
+ width*height*stg_type_size*strlen(map), length);
1249
+ }
1250
+
1251
+ Data_Get_Struct(obj, Wand, wand);
1252
+ MagickResetIterator(wand->magickwand);
1253
+ MagickImportImagePixels(wand->magickwand, x, y, width, height, map, stg_type, data);
1254
+ mwr_check_magickwand_error(wand->magickwand);
1255
+
1256
+ return obj;
1257
+ }
1258
+
1259
+
1260
+
1261
+
1262
+ /*
1263
+ * wand.hash is the same as wand.signature.hash
1264
+ */
1265
+ static VALUE wand_hash(VALUE obj)
1266
+ {
1267
+ volatile VALUE signature;
1268
+ Wand *wand;
1269
+
1270
+ Data_Get_Struct(obj, Wand, wand);
1271
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
1272
+ {
1273
+ return INT2FIX(0);
1274
+ }
1275
+
1276
+ signature = wand_signature(obj);
1277
+ return INT2FIX(rb_str_hash(signature));
1278
+ }
1279
+
1280
+
1281
+
1282
+
1283
+ /*
1284
+ * wand.insert(index, wand2)
1285
+ * Inserts images from wand2 before the image at the given index, modifying
1286
+ * wand.
1287
+ */
1288
+ static VALUE wand_insert(VALUE obj, VALUE index, VALUE obj2)
1289
+ {
1290
+ Wand *wand, *wand2;
1291
+ long pos;
1292
+
1293
+ Data_Get_Struct(obj, Wand, wand);
1294
+ Data_Get_Struct(obj2, Wand, wand2);
1295
+
1296
+ rb_check_frozen(obj);
1297
+
1298
+ pos = FIX2LONG(index);
1299
+ if (pos == -1)
1300
+ {
1301
+ pos = (long) MagickGetNumberImages(wand->magickwand);
1302
+ }
1303
+ else if (pos < 0)
1304
+ {
1305
+ pos += 1;
1306
+ }
1307
+
1308
+ return splice(obj, pos, 0, obj2);
1309
+ }
1310
+
1311
+
1312
+
1313
+
1314
+ /*
1315
+ * The inspect string is intended to be quite similar to the output
1316
+ * of the identify command, without the -verbose option.
1317
+ * Largely lifted from ImageMagick's IdentifyImage function.
1318
+ */
1319
+ static VALUE wand_inspect(VALUE obj)
1320
+ {
1321
+ Wand *wand;
1322
+ volatile VALUE inspect = rb_str_new2("(");
1323
+ Image *image;
1324
+ double elapsed_time;
1325
+ double user_time;
1326
+ unsigned long index = 0UL;
1327
+ unsigned long columns, rows;
1328
+ unsigned long width, height;
1329
+ long x, y;
1330
+ char *filename, *format;
1331
+ char buff[MaxTextExtent];
1332
+ ImageType image_type;
1333
+
1334
+ Data_Get_Struct(obj, Wand, wand);
1335
+
1336
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
1337
+ {
1338
+ return rb_str_new2("()");
1339
+ }
1340
+
1341
+ MagickResetIterator(wand->magickwand);
1342
+
1343
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
1344
+ {
1345
+ if (index > 0UL)
1346
+ {
1347
+ (void) rb_str_cat(inspect, "\n", 1);
1348
+ }
1349
+
1350
+ image = GetImageFromMagickWand(wand->magickwand);
1351
+ mwr_check_magickwand_error(wand->magickwand);
1352
+
1353
+ // magick_filename, filename
1354
+ if (*image->magick_filename != '\0')
1355
+ {
1356
+ filename = MagickGetImageFilename(wand->magickwand);
1357
+ if (strcmp(image->magick_filename, filename) != 0)
1358
+ {
1359
+ (void) rb_str_cat2(inspect, image->magick_filename);
1360
+ (void) rb_str_cat(inspect, "=>", 2);
1361
+ }
1362
+ if (index == 0)
1363
+ {
1364
+ (void) rb_str_cat2(inspect, filename);
1365
+ }
1366
+ else
1367
+ {
1368
+ (void) rb_str_cat2(inspect, filename);
1369
+ (void) sprintf(buff, "[%lu]", index);
1370
+ (void) rb_str_cat2(inspect, buff);
1371
+ }
1372
+ MagickRelinquishMemory(filename);
1373
+ }
1374
+
1375
+ // format
1376
+ format = MagickGetImageFormat(wand->magickwand);
1377
+ if (*format != '\0')
1378
+ {
1379
+ (void) sprintf(buff, " %s", format);
1380
+ (void) rb_str_cat2(inspect, buff);
1381
+ }
1382
+ MagickRelinquishMemory(format);
1383
+ (void) rb_str_cat(inspect, " ", 1);
1384
+
1385
+ // dimensions
1386
+ columns = MagickGetImageWidth(wand->magickwand);
1387
+ rows = MagickGetImageHeight(wand->magickwand);
1388
+ if (image->magick_columns != 0 || image->magick_rows != 0)
1389
+ {
1390
+ if (image->magick_columns != columns || image->magick_rows != rows)
1391
+ {
1392
+ (void) sprintf(buff, "%lux%lu=>", image->magick_columns, image->magick_rows);
1393
+ (void) rb_str_cat2(inspect, buff);
1394
+ }
1395
+ }
1396
+ (void) sprintf(buff, "%lux%lu ", columns, rows);
1397
+ (void) rb_str_cat2(inspect, buff);
1398
+
1399
+ // page
1400
+ MagickGetImagePage(wand->magickwand, &width, &height, &x, &y);
1401
+ if (width != 0 || height != 0 || x != 0 || y != 0)
1402
+ {
1403
+ (void) sprintf(buff,"%lux%lu%+ld%+ld ", width, height, x, y);
1404
+ (void) rb_str_cat2(inspect, buff);
1405
+ }
1406
+
1407
+ (void) sprintf(buff, "%lu-bit ", MagickGetImageDepth(wand->magickwand));
1408
+ (void) rb_str_cat2(inspect, buff);
1409
+
1410
+ image_type = MagickGetImageType(wand->magickwand);
1411
+ if (image_type != UndefinedType)
1412
+ {
1413
+ (void) rb_str_cat2(inspect, mwr_imagetype_to_s(image_type));
1414
+ (void) rb_str_cat(inspect, " ", 1);
1415
+ }
1416
+
1417
+ if (image->storage_class == DirectClass)
1418
+ {
1419
+ (void) rb_str_cat2(inspect, "DirectClass ");
1420
+ if (image->total_colors != 0)
1421
+ {
1422
+ (void) rb_str_cat2(inspect, mwr_format_size(image->total_colors, buff));
1423
+ }
1424
+ }
1425
+ else
1426
+ {
1427
+ (void) rb_str_cat2(inspect, "PseudoClass ");
1428
+ if (image->total_colors <= image->colors)
1429
+ {
1430
+ (void) sprintf(buff, "%luc", image->colors);
1431
+ }
1432
+ else
1433
+ {
1434
+ (void) sprintf(buff, "%lu=>%luc", image->total_colors, image->colors);
1435
+ }
1436
+ (void) rb_str_cat2(inspect, buff);
1437
+ }
1438
+
1439
+ if (image->error.mean_error_per_pixel != 0.0)
1440
+ {
1441
+ (void) sprintf(buff, ".%ld/%f/%fdb",(long) (image->error.mean_error_per_pixel+0.5),
1442
+ image->error.normalized_mean_error, image->error.normalized_maximum_error);
1443
+ (void) rb_str_cat2(inspect, buff);
1444
+ }
1445
+
1446
+
1447
+ if (GetBlobSize(image) != 0)
1448
+ {
1449
+ (void) rb_str_cat(inspect, " ", 1);
1450
+ (void) rb_str_cat2(inspect, mwr_format_size(GetBlobSize(image), buff));
1451
+ }
1452
+
1453
+ if (elapsed_time > 0.06)
1454
+ {
1455
+ elapsed_time = GetElapsedTime(&image->timer);
1456
+ user_time = GetUserTime(&image->timer);
1457
+ (void) sprintf(buff, " %0.3fu %ld:%02ld", user_time,
1458
+ (long) (elapsed_time/60.0+0.5),(long) ceil(fmod(elapsed_time, 60.0)));
1459
+ (void) rb_str_cat2(inspect, buff);
1460
+ }
1461
+
1462
+ index += 1UL;
1463
+ }
1464
+
1465
+ rb_str_cat(inspect, ")", 1);
1466
+ return inspect;
1467
+ }
1468
+
1469
+
1470
+
1471
+
1472
+ /*
1473
+ * wand = MagickWand::Wand.new
1474
+ * accepts no arguments
1475
+ * wand_allocate just creates an empty Wand structure. Here we add a new, empty
1476
+ * MagickWand.
1477
+ */
1478
+ static VALUE wand_initialize(VALUE obj)
1479
+ {
1480
+ Wand *wand;
1481
+
1482
+ Data_Get_Struct(obj, Wand, wand);
1483
+ wand->magickwand = NewMagickWand();
1484
+
1485
+ return obj;
1486
+ }
1487
+
1488
+
1489
+
1490
+
1491
+ /*
1492
+ * wand2 = wand.dup
1493
+ * wand2 = wand.clone
1494
+ */
1495
+ static VALUE wand_initialize_copy(VALUE obj, VALUE obj2)
1496
+ {
1497
+ Wand *wand, *wand2;
1498
+
1499
+ if (obj == obj2)
1500
+ {
1501
+ return obj;
1502
+ }
1503
+
1504
+ Data_Get_Struct(obj, Wand, wand);
1505
+ Data_Get_Struct(obj2, Wand, wand2);
1506
+
1507
+ wand->magickwand = CloneMagickWand(wand2->magickwand);
1508
+ return obj;
1509
+ }
1510
+
1511
+
1512
+
1513
+
1514
+ /*
1515
+ * Wand#length - the number of images in the wand
1516
+ */
1517
+ static VALUE wand_length(VALUE obj)
1518
+ {
1519
+ Wand *wand;
1520
+
1521
+ Data_Get_Struct(obj, Wand, wand);
1522
+ return LONG2FIX((long)MagickGetNumberImages(wand->magickwand));
1523
+ }
1524
+
1525
+
1526
+
1527
+
1528
+ /*
1529
+ * Brightness is required. Saturation and hue default to 100% (no change).
1530
+ * Same defaults as the -modulate option. See magick/enhance.c.
1531
+ */
1532
+ static VALUE wand_modulate(int argc, VALUE *argv, VALUE obj)
1533
+ {
1534
+ Wand *wand;
1535
+ volatile VALUE brightness, saturation, hue;
1536
+ double h, s, l;
1537
+
1538
+ rb_check_frozen(obj);
1539
+
1540
+ (void) rb_scan_args(argc, argv, "12", &brightness, &saturation, &hue);
1541
+ l = NUM2DBL(brightness);
1542
+ s = saturation != Qnil ? NUM2DBL(saturation) : 100.0;
1543
+ h = hue != Qnil ? NUM2DBL(hue) : 100.0;
1544
+
1545
+ Data_Get_Struct(obj, Wand, wand);
1546
+ MagickResetIterator(wand->magickwand);
1547
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
1548
+ {
1549
+ MagickModulateImage(wand->magickwand, l, s, h);
1550
+ mwr_check_magickwand_error(wand->magickwand);
1551
+ }
1552
+
1553
+ return obj;
1554
+ }
1555
+
1556
+
1557
+
1558
+
1559
+ /*
1560
+ * Construct a new Wand object from a MagickWand.
1561
+ */
1562
+ static VALUE wand_new(MagickWand *magickwand)
1563
+ {
1564
+ VALUE obj;
1565
+ Wand *wand;
1566
+
1567
+ obj = Data_Make_Struct(mwr_cWand, Wand, NULL, wand_destroy, wand);
1568
+ if (magickwand)
1569
+ {
1570
+ if (IsMagickWand(magickwand) == MagickTrue)
1571
+ {
1572
+ wand->magickwand = magickwand;
1573
+ }
1574
+ else
1575
+ {
1576
+ rb_bug("invalid MagickWand used for object allocation");
1577
+ }
1578
+ }
1579
+ else
1580
+ {
1581
+ wand->magickwand = NewMagickWand();
1582
+ }
1583
+
1584
+ return obj;
1585
+ }
1586
+
1587
+
1588
+
1589
+
1590
+ /*
1591
+ * [width, height, x, y] = wand.page
1592
+ */
1593
+ static VALUE wand_page(VALUE obj)
1594
+ {
1595
+ Wand *wand;
1596
+ volatile VALUE page;
1597
+ unsigned long width, height;
1598
+ long x, y;
1599
+
1600
+ Data_Get_Struct(obj, Wand, wand);
1601
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
1602
+ {
1603
+ return Qnil;
1604
+ }
1605
+
1606
+ MagickResetIterator(wand->magickwand);
1607
+ MagickGetImagePage(wand->magickwand, &width, &height, &x, &y);
1608
+ mwr_check_magickwand_error(wand->magickwand);
1609
+
1610
+ page = rb_ary_new2(4);
1611
+ rb_ary_push(page, ULONG2NUM(width));
1612
+ rb_ary_push(page, ULONG2NUM(height));
1613
+ rb_ary_push(page, LONG2NUM(x));
1614
+ rb_ary_push(page, LONG2NUM(y));
1615
+
1616
+ return page;
1617
+ }
1618
+
1619
+
1620
+
1621
+
1622
+ /*
1623
+ * objc = obja + objb
1624
+ * creates a new wand from the images in obja and objb
1625
+ */
1626
+ static VALUE wand_plus(VALUE obj, VALUE obj2)
1627
+ {
1628
+ Wand *wand, *wand2;
1629
+ MagickWand *temp, *new_magickwand;
1630
+
1631
+ Data_Get_Struct(obj, Wand, wand);
1632
+ Data_Get_Struct(obj2, Wand, wand2);
1633
+
1634
+ new_magickwand = CloneMagickWand(wand->magickwand);
1635
+ MagickResetIterator(wand2->magickwand);
1636
+ while (MagickNextImage(wand2->magickwand) != MagickFalse)
1637
+ {
1638
+ MagickSetLastIterator(new_magickwand);
1639
+ temp = MagickGetImage(wand2->magickwand);
1640
+ mwr_check_magickwand_error(wand2->magickwand);
1641
+ MagickAddImage(new_magickwand, temp);
1642
+ temp = DestroyMagickWand(temp);
1643
+ mwr_check_magickwand_error(new_magickwand);
1644
+ }
1645
+
1646
+ return wand_new(new_magickwand);
1647
+ }
1648
+
1649
+
1650
+
1651
+
1652
+ /*
1653
+ * shared between MagickReadImage and MagickPingImage
1654
+ */
1655
+ static VALUE read(int argc, VALUE *argv, VALUE obj, MagickBooleanType (*reader)(MagickWand *, const char *))
1656
+ {
1657
+ volatile VALUE fname, options;
1658
+ Wand *wand;
1659
+ const char *filename;
1660
+
1661
+ rb_check_frozen(obj);
1662
+
1663
+ (void) rb_scan_args(argc, argv, "11", &fname, &options);
1664
+ filename = StringValuePtr(fname);
1665
+ mwr_process_options(obj, options);
1666
+
1667
+ Data_Get_Struct(obj, Wand, wand);
1668
+
1669
+ // Add to the end.
1670
+ MagickSetLastIterator(wand->magickwand);
1671
+ (void) (*reader)(wand->magickwand, filename);
1672
+ mwr_check_magickwand_error(wand->magickwand);
1673
+
1674
+ return obj;
1675
+ }
1676
+
1677
+
1678
+
1679
+
1680
+ /*
1681
+ * Does not read pixel data. Gets only the image dimensions, type, and length. .
1682
+ * An image filename is mandantory, optionally followed by an options hash.
1683
+ * Multiple reads are possible. Each call adds the new images to the wand . .
1684
+ */
1685
+ static VALUE wand_ping(int argc, VALUE *argv, VALUE obj)
1686
+ {
1687
+ return read(argc, argv, obj, MagickPingImage);
1688
+ }
1689
+
1690
+
1691
+
1692
+
1693
+ /*
1694
+ * An image filename is mandantory, optionally followed by an options hash.
1695
+ * Multiple reads are possible. Each call adds the new images to the wand.
1696
+ */
1697
+ static VALUE wand_read(int argc, VALUE *argv, VALUE obj)
1698
+ {
1699
+ return read(argc, argv, obj, MagickReadImage);
1700
+ }
1701
+
1702
+
1703
+
1704
+
1705
+ /*
1706
+ * shared with wand_read_string and wand_ping_string
1707
+ */
1708
+ static VALUE read_string(int argc, VALUE *argv, VALUE obj,
1709
+ MagickBooleanType(*reader)(MagickWand *, const void *, size_t))
1710
+ {
1711
+ VALUE string, options;
1712
+ const void *blob;
1713
+ size_t length;
1714
+ Wand *wand;
1715
+
1716
+ rb_check_frozen(obj);
1717
+ (void)rb_scan_args(argc, argv, "11", &string, &options);
1718
+
1719
+ StringValue(string);
1720
+ blob = (void *) RSTRING_PTR(string);
1721
+ length = (size_t) RSTRING_LEN(string);
1722
+
1723
+ mwr_process_options(obj, options);
1724
+
1725
+ Data_Get_Struct(obj, Wand, wand);
1726
+
1727
+ // Add to the end.
1728
+ MagickSetLastIterator(wand->magickwand);
1729
+ (void) (*reader)(wand->magickwand, blob, length);
1730
+ mwr_check_magickwand_error(wand->magickwand);
1731
+
1732
+ return obj;
1733
+ }
1734
+
1735
+
1736
+
1737
+
1738
+ /*
1739
+ * Does not read pixel data. Gets only the image dimensions, type, and length. .
1740
+ * wand.ping_string(string,...)
1741
+ * Reads one or more images from the string into the wand and adds them
1742
+ * to the *end* of the image list.
1743
+ */
1744
+ static VALUE wand_ping_string(int argc, VALUE *argv, VALUE obj)
1745
+ {
1746
+ return read_string(argc, argv, obj, MagickPingImageBlob);
1747
+ }
1748
+
1749
+
1750
+
1751
+ /*
1752
+ * wand.read_string(string,...)
1753
+ * Reads one or more images from the string into the wand and adds them
1754
+ * to the *end* of the image list.
1755
+ */
1756
+ static VALUE wand_read_string(int argc, VALUE *argv, VALUE obj)
1757
+ {
1758
+ return read_string(argc, argv, obj, MagickReadImageBlob);
1759
+ }
1760
+
1761
+
1762
+
1763
+
1764
+ /*
1765
+ * resizes all the images in the wand to the specified dimensions
1766
+ * wand.resize(width, height=width, options)
1767
+ * valid options are :filter, :blur
1768
+ */
1769
+ static VALUE wand_resize(int argc, VALUE *argv, VALUE obj)
1770
+ {
1771
+ Wand *wand;
1772
+ volatile VALUE width, height, options;
1773
+ VALUE v;
1774
+ double blur = 1.0;
1775
+ unsigned long columns, rows;
1776
+ FilterTypes filter = UndefinedFilter;
1777
+
1778
+ rb_check_frozen(obj);
1779
+
1780
+ (void) rb_scan_args(argc, argv, "12", &width, &height, &options);
1781
+ columns = NUM2ULONG(width);
1782
+ rows = (height != Qnil) ? NUM2ULONG(height) : columns;
1783
+ (void) mwr_get_option(options, "filter", &v);
1784
+ filter = mwr_string_to_filtertypes(v, UndefinedFilter, RaiseUndefinedOption);
1785
+ if (mwr_get_option(options, "blur", &v) && v != Qnil)
1786
+ {
1787
+ blur = NUM2DBL(v);
1788
+ }
1789
+
1790
+ Data_Get_Struct(obj, Wand, wand);
1791
+
1792
+ MagickResetIterator(wand->magickwand);
1793
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
1794
+ {
1795
+ MagickResizeImage(wand->magickwand, columns, rows, filter, blur);
1796
+ mwr_check_magickwand_error(wand->magickwand);
1797
+ }
1798
+
1799
+ return obj;
1800
+ }
1801
+
1802
+
1803
+
1804
+ /*
1805
+ * x-res, y-res = wand.resolution
1806
+ * Retreives the resolution from the first image
1807
+ */
1808
+ static VALUE wand_resolution(VALUE obj)
1809
+ {
1810
+ Wand *wand;
1811
+ double x_res, y_res;
1812
+
1813
+ Data_Get_Struct(obj, Wand, wand);
1814
+ MagickResetIterator(wand->magickwand);
1815
+ (void) MagickGetImageResolution(wand->magickwand, &x_res, &y_res);
1816
+ mwr_check_magickwand_error(wand->magickwand);
1817
+ return rb_ary_new3(2, rb_float_new(x_res), rb_float_new(y_res));
1818
+ }
1819
+
1820
+
1821
+
1822
+
1823
+ /*
1824
+ * wand.rotate(degrees, background = "white")
1825
+ */
1826
+ static VALUE wand_rotate(int argc, VALUE *argv, VALUE obj)
1827
+ {
1828
+ Wand *wand;
1829
+ VALUE amount, background;
1830
+ double degrees;
1831
+ char *background_color = "white";
1832
+ PixelWand *pixelwand;
1833
+
1834
+ rb_check_frozen(obj);
1835
+
1836
+ (void) rb_scan_args(argc, argv, "11", &amount, &background);
1837
+ degrees = NUM2DBL(amount);
1838
+ if (background != Qnil)
1839
+ {
1840
+ background_color = StringValuePtr(background);
1841
+ }
1842
+
1843
+ pixelwand = NewPixelWand();
1844
+ PixelSetColor(pixelwand, background_color);
1845
+ mwr_check_pixelwand_error(pixelwand);
1846
+
1847
+ Data_Get_Struct(obj, Wand, wand);
1848
+
1849
+ MagickResetIterator(wand->magickwand);
1850
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
1851
+ {
1852
+ MagickRotateImage(wand->magickwand, pixelwand, degrees);
1853
+ mwr_check_magickwand_error(wand->magickwand);
1854
+ }
1855
+
1856
+ DestroyPixelWand(pixelwand);
1857
+
1858
+ return obj;
1859
+ }
1860
+
1861
+
1862
+
1863
+
1864
+ /*
1865
+ * :colorspace => "colorspace"
1866
+ */
1867
+ #if defined(HAVE_MAGICKSETCOLORSPACE)
1868
+ static VALUE wand_set_colorspace(VALUE obj, VALUE colorspace)
1869
+ {
1870
+ Wand *wand;
1871
+ ColorspaceType type;
1872
+
1873
+ rb_check_frozen(obj);
1874
+
1875
+ type = mwr_string_to_colorspacetype(colorspace, UndefinedColorspace, RaiseUndefinedOption);
1876
+ Data_Get_Struct(obj, Wand, wand);
1877
+ MagickSetColorspace(wand->magickwand, type);
1878
+ mwr_check_magickwand_error(wand->magickwand);
1879
+ return obj;
1880
+ }
1881
+ #endif
1882
+
1883
+
1884
+
1885
+
1886
+ /*
1887
+ * :compress => "compression"
1888
+ */
1889
+ static VALUE wand_set_compress(VALUE obj, VALUE compression)
1890
+ {
1891
+ Wand *wand;
1892
+ CompressionType type;
1893
+
1894
+ type = mwr_string_to_compressiontype(compression, UndefinedCompression, RaiseUndefinedOption);
1895
+ rb_check_frozen(obj);
1896
+ Data_Get_Struct(obj, Wand, wand);
1897
+ MagickSetCompression(wand->magickwand, type);
1898
+ mwr_check_magickwand_error(wand->magickwand);
1899
+ return obj;
1900
+ }
1901
+
1902
+
1903
+
1904
+
1905
+ /*
1906
+ * :depth => depth
1907
+ */
1908
+ static VALUE wand_set_depth(VALUE obj, VALUE depth)
1909
+ {
1910
+ Wand *wand;
1911
+
1912
+ rb_check_frozen(obj);
1913
+ Data_Get_Struct(obj, Wand, wand);
1914
+ MagickSetDepth(wand->magickwand, NUM2ULONG(depth));
1915
+ mwr_check_magickwand_error(wand->magickwand);
1916
+ return obj;
1917
+ }
1918
+
1919
+
1920
+
1921
+
1922
+ /*
1923
+ * :format => "format"
1924
+ */
1925
+ static VALUE wand_set_format(VALUE obj, VALUE format)
1926
+ {
1927
+ Wand *wand;
1928
+ const MagickInfo *magick;
1929
+ char *fmt;
1930
+ ExceptionInfo exception;
1931
+
1932
+ rb_check_frozen(obj);
1933
+
1934
+ fmt = StringValuePtr(format);
1935
+ GetExceptionInfo(&exception);
1936
+ magick = GetMagickInfo(fmt, &exception);
1937
+ DestroyExceptionInfo(&exception);
1938
+ if (!magick)
1939
+ {
1940
+ rb_raise(rb_eArgError, "unknown format %s", fmt);
1941
+ }
1942
+
1943
+ Data_Get_Struct(obj, Wand, wand);
1944
+ MagickSetFormat(wand->magickwand, fmt);
1945
+ mwr_check_magickwand_error(wand->magickwand);
1946
+ return obj;
1947
+ }
1948
+
1949
+
1950
+
1951
+
1952
+ /*
1953
+ * :quality => quality
1954
+ */
1955
+ static VALUE wand_set_quality(VALUE obj, VALUE quality)
1956
+ {
1957
+ Wand *wand;
1958
+
1959
+ rb_check_frozen(obj);
1960
+ Data_Get_Struct(obj, Wand, wand);
1961
+ MagickSetCompressionQuality(wand->magickwand, NUM2ULONG(quality));
1962
+ mwr_check_magickwand_error(wand->magickwand);
1963
+ return obj;
1964
+ }
1965
+
1966
+
1967
+
1968
+ /*
1969
+ * :size => "WxH"
1970
+ */
1971
+ static VALUE wand_set_size(VALUE obj, VALUE size)
1972
+ {
1973
+ Wand *wand;
1974
+ GeometryInfo geometry;
1975
+ MagickStatusType flags;
1976
+ unsigned long columns = 0, rows = 0;
1977
+
1978
+ memset(&geometry, 0, sizeof(geometry));
1979
+ flags = ParseGeometry(StringValuePtr(size), &geometry);
1980
+ if (flags == NoValue)
1981
+ {
1982
+ mwr_option_value_error(":size", StringValuePtr(size));
1983
+ }
1984
+
1985
+ columns = geometry.rho;
1986
+ rows = geometry.sigma;
1987
+
1988
+ rb_check_frozen(obj);
1989
+ Data_Get_Struct(obj, Wand, wand);
1990
+ MagickSetSize(wand->magickwand, columns, rows);
1991
+ mwr_check_magickwand_error(wand->magickwand);
1992
+ return obj;
1993
+ }
1994
+
1995
+
1996
+
1997
+
1998
+ /*
1999
+ * :type = "type"
2000
+ */
2001
+ static VALUE wand_set_type(VALUE obj, VALUE type)
2002
+ {
2003
+ Wand *wand;
2004
+ ImageType image_type = UndefinedType;
2005
+
2006
+ image_type = mwr_string_to_imagetype(type, UndefinedType, RaiseUndefinedOption);
2007
+ Data_Get_Struct(obj, Wand, wand);
2008
+ MagickSetType(wand->magickwand, image_type);
2009
+ mwr_check_magickwand_error(wand->magickwand);
2010
+ return obj;
2011
+ }
2012
+
2013
+
2014
+
2015
+
2016
+ /*
2017
+ * wand.sharpen(radius, sigma, :channel="default")
2018
+ */
2019
+ static VALUE wand_sharpen(int argc, VALUE *argv, VALUE obj)
2020
+ {
2021
+ return convolve(argc, argv, obj, MagickSharpenImageChannel);
2022
+ }
2023
+
2024
+
2025
+
2026
+
2027
+ /*
2028
+ * return 64-hex digit signature of the first image.
2029
+ * used to "compare" two wands for equality.
2030
+ * returns nil if no images in wand.
2031
+ * note: this is relatively slow operation the first time it's called
2032
+ */
2033
+ static VALUE wand_signature(VALUE obj)
2034
+ {
2035
+ Wand *wand;
2036
+ char *sig;
2037
+ volatile VALUE signature;
2038
+
2039
+ Data_Get_Struct(obj, Wand, wand);
2040
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
2041
+ {
2042
+ return Qnil;
2043
+ }
2044
+
2045
+ MagickSetFirstIterator(wand->magickwand);
2046
+ sig = MagickGetImageSignature(wand->magickwand);
2047
+ mwr_check_magickwand_error(wand->magickwand);
2048
+ signature = rb_str_new2(sig);
2049
+ RelinquishMagickMemory(sig);
2050
+ return signature;
2051
+ }
2052
+
2053
+
2054
+
2055
+
2056
+ /*
2057
+ * wand.slice!(2) => deletes and returns a wand containing the 3rd image
2058
+ * wand.slice!(3..6) => deletes and returns a wand containing images 4, 5, 6
2059
+ *
2060
+ * This code is essentially identical to rb_str_slice_bang in string.c.
2061
+ */
2062
+ static VALUE wand_slice_bang(int argc, VALUE *argv, VALUE obj)
2063
+ {
2064
+ volatile VALUE result;
2065
+ VALUE buf[3];
2066
+ int i;
2067
+
2068
+ if (argc < 1 || 2 < argc)
2069
+ {
2070
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
2071
+ }
2072
+
2073
+ rb_check_frozen(obj);
2074
+
2075
+ for (i = 0; i < argc; i++)
2076
+ {
2077
+ buf[i] = argv[i];
2078
+ }
2079
+ buf[i] = wand_new(NULL);
2080
+ result = wand_aref(argc, buf, obj);
2081
+ if (!NIL_P(result))
2082
+ {
2083
+ wand_aset(argc+1, buf, obj);
2084
+ }
2085
+
2086
+ return result;
2087
+ }
2088
+
2089
+
2090
+
2091
+
2092
+ /*
2093
+ * wand.strip()
2094
+ */
2095
+ static VALUE wand_strip(VALUE obj)
2096
+ {
2097
+ Wand *wand;
2098
+
2099
+ rb_check_frozen(obj);
2100
+ Data_Get_Struct(obj, Wand, wand);
2101
+ MagickResetIterator(wand->magickwand);
2102
+ while (MagickNextImage(wand->magickwand) != MagickFalse)
2103
+ {
2104
+ MagickStripImage(wand->magickwand);
2105
+ mwr_check_magickwand_error(wand->magickwand);
2106
+ }
2107
+
2108
+ return obj;
2109
+ }
2110
+
2111
+
2112
+
2113
+
2114
+ /*
2115
+ * type = wand.type
2116
+ */
2117
+ static VALUE wand_type(VALUE obj)
2118
+ {
2119
+ Wand *wand;
2120
+ ImageType image_type;
2121
+
2122
+ Data_Get_Struct(obj, Wand, wand);
2123
+ if (MagickGetNumberImages(wand->magickwand) == 0UL)
2124
+ {
2125
+ return Qnil;
2126
+ }
2127
+
2128
+ MagickResetIterator(wand->magickwand);
2129
+ image_type = MagickGetImageType(wand->magickwand);
2130
+ mwr_check_magickwand_error(wand->magickwand);
2131
+
2132
+ return rb_str_new2(mwr_imagetype_to_s(image_type));
2133
+ }
2134
+
2135
+
2136
+
2137
+
2138
+ /*
2139
+ * string = wand.write_string
2140
+ * returns the images as a string
2141
+ */
2142
+ static VALUE wand_write_string(int argc, VALUE *argv, VALUE obj)
2143
+ {
2144
+ Wand *wand;
2145
+ VALUE options;
2146
+ volatile VALUE str;
2147
+ unsigned char *blob;
2148
+ size_t length;
2149
+ unsigned long nimages;
2150
+
2151
+ Data_Get_Struct(obj, Wand, wand);
2152
+ nimages = MagickGetNumberImages(wand->magickwand);
2153
+ if (nimages == 0UL)
2154
+ {
2155
+ return Qnil;
2156
+ }
2157
+
2158
+ (void) rb_scan_args(argc, argv, "01", &options);
2159
+ mwr_process_options(obj, options);
2160
+
2161
+ MagickResetIterator(wand->magickwand);
2162
+ if (nimages == 1UL)
2163
+ {
2164
+ blob = MagickGetImageBlob(wand->magickwand, &length);
2165
+ }
2166
+ else
2167
+ {
2168
+ blob = MagickGetImagesBlob(wand->magickwand, &length);
2169
+ }
2170
+
2171
+ str = rb_str_new((char *)blob, (long)length);
2172
+ RelinquishMagickMemory(blob);
2173
+
2174
+ return str;
2175
+ }
2176
+
2177
+
2178
+
2179
+
2180
+ /*
2181
+ * wand.write(filename, :adjoin=>true)
2182
+ */
2183
+ static VALUE wand_write(int argc, VALUE *argv, VALUE obj)
2184
+ {
2185
+ Wand *wand;
2186
+ volatile VALUE name, options;
2187
+ VALUE v;
2188
+ MagickBooleanType adjoin = MagickTrue;
2189
+
2190
+ (void) rb_scan_args(argc, argv, "11", &name, &options);
2191
+ if (mwr_get_option(options, "adjoin", &v))
2192
+ {
2193
+ adjoin = RTEST(v) ? MagickTrue : MagickFalse;
2194
+ }
2195
+ mwr_process_options(obj, options);
2196
+
2197
+ Data_Get_Struct(obj, Wand, wand);
2198
+ MagickResetIterator(wand->magickwand);
2199
+ MagickWriteImages(wand->magickwand, StringValuePtr(name), adjoin);
2200
+ mwr_check_magickwand_error(wand->magickwand);
2201
+
2202
+ return obj;
2203
+ }
2204
+
2205
+
2206
+
2207
+
2208
+ /*
2209
+ * MagickWand::Wand class
2210
+ * includes Comparable and Enumerable
2211
+ */
2212
+ void mwr_init_Wand(void)
2213
+ {
2214
+ mwr_cWand = rb_define_class_under(mwr_mMagickWand, "Wand", rb_cObject);
2215
+ rb_include_module(mwr_cWand, rb_mComparable);
2216
+ rb_include_module(mwr_cWand, rb_mEnumerable);
2217
+ rb_define_alloc_func(mwr_cWand, wand_allocate);
2218
+
2219
+ // Support for Enumerable, Comparable, etc.
2220
+ // Methods in Object that must be overridden per class
2221
+ rb_define_method(mwr_cWand, "[]", wand_aref, -1);
2222
+ rb_define_method(mwr_cWand, "[]=", wand_aset, -1);
2223
+ rb_define_method(mwr_cWand, "<=>", wand_cmp, 1);
2224
+ rb_define_method(mwr_cWand, "<<", wand_concat, 1);
2225
+ rb_define_method(mwr_cWand, "each", wand_each, 0);
2226
+ rb_define_method(mwr_cWand, "empty?", wand_empty, 0);
2227
+ rb_define_method(mwr_cWand, "eql?", wand_eql, 1);
2228
+ rb_define_method(mwr_cWand, "hash", wand_hash, 0);
2229
+ rb_define_method(mwr_cWand, "initialize", wand_initialize, 0);
2230
+ rb_define_method(mwr_cWand, "initialize_copy", wand_initialize_copy, 1);
2231
+ rb_define_method(mwr_cWand, "insert", wand_insert, 2);
2232
+ rb_define_method(mwr_cWand, "inspect", wand_inspect, 0);
2233
+ rb_define_method(mwr_cWand, "length", wand_length, 0);
2234
+ rb_define_method(mwr_cWand, "+", wand_plus, 1);
2235
+ rb_define_method(mwr_cWand, "slice!", wand_slice_bang, -1);
2236
+ rb_define_alias(mwr_cWand, "concat", "<<");
2237
+ // Make an alias for Object#display
2238
+ rb_define_alias(mwr_cWand, "__display__", "display");
2239
+ rb_define_alias(mwr_cWand, "size", "length");
2240
+ rb_define_alias(mwr_cWand, "slice", "[]");
2241
+ // Object#type is not defined in 1.9.1
2242
+ #if defined(HAVE_RB_OBJ_TYPE)
2243
+ rb_define_alias(mwr_cWand, "__type__", "type");
2244
+ #endif
2245
+
2246
+ // Private property setters are called during option processing
2247
+ #if defined(HAVE_MAGICKSETCOLORSPACE)
2248
+ rb_define_private_method(mwr_cWand, "set_colorspace", wand_set_colorspace, 1);
2249
+ #endif
2250
+ rb_define_private_method(mwr_cWand, "set_compress", wand_set_compress, 1);
2251
+ rb_define_private_method(mwr_cWand, "set_depth", wand_set_depth, 1);
2252
+ rb_define_private_method(mwr_cWand, "set_format", wand_set_format, 1);
2253
+ rb_define_private_method(mwr_cWand, "set_quality", wand_set_quality, 1);
2254
+ rb_define_private_method(mwr_cWand, "set_size", wand_set_size, 1);
2255
+ rb_define_private_method(mwr_cWand, "set_type", wand_set_type, 1);
2256
+
2257
+ // Other private methods
2258
+ rb_define_private_method(mwr_cWand, "get_resize_geometry", wand_get_resize_geometry, 1);
2259
+
2260
+ // Methods that bind to ImageMagick's MagickWand API
2261
+ rb_define_method(mwr_cWand, "add_canvas", wand_add_canvas, -1);
2262
+ rb_define_method(mwr_cWand, "animate", wand_animate, -1);
2263
+ rb_define_method(mwr_cWand, "annotate", wand_annotate, -1);
2264
+ rb_define_method(mwr_cWand, "background", wand_background, 0);
2265
+ rb_define_method(mwr_cWand, "blur", wand_blur, -1);
2266
+ rb_define_method(mwr_cWand, "border", wand_border, -1);
2267
+ rb_define_method(mwr_cWand, "colors", wand_colors, 0);
2268
+ rb_define_method(mwr_cWand, "comment", wand_comment, 0);
2269
+ rb_define_method(mwr_cWand, "comment=", wand_comment_set, 1);
2270
+ rb_define_method(mwr_cWand, "compare", wand_compare, -1);
2271
+ rb_define_method(mwr_cWand, "composite", wand_composite, -1);
2272
+ rb_define_method(mwr_cWand, "crop", wand_crop, -1);
2273
+ rb_define_method(mwr_cWand, "depth", wand_depth, 0);
2274
+ rb_define_method(mwr_cWand, "dimensions", wand_dimensions, 0);
2275
+ rb_define_method(mwr_cWand, "display", wand_display, 0);
2276
+ rb_define_method(mwr_cWand, "export_pixels", wand_export_pixels, -1);
2277
+ rb_define_method(mwr_cWand, "filename", wand_filename, 0);
2278
+ rb_define_method(mwr_cWand, "format", wand_format, 0);
2279
+ rb_define_method(mwr_cWand, "import_pixels", wand_import_pixels, -1);
2280
+ rb_define_method(mwr_cWand, "modulate", wand_modulate, -1);
2281
+ rb_define_method(mwr_cWand, "page", wand_page, 0);
2282
+ rb_define_method(mwr_cWand, "ping", wand_ping, -1);
2283
+ rb_define_method(mwr_cWand, "ping_string", wand_ping_string, -1);
2284
+ rb_define_method(mwr_cWand, "read", wand_read, -1);
2285
+ rb_define_method(mwr_cWand, "read_string", wand_read_string, -1);
2286
+ rb_define_method(mwr_cWand, "resize", wand_resize, -1);
2287
+ rb_define_method(mwr_cWand, "resolution", wand_resolution, 0);
2288
+ rb_define_method(mwr_cWand, "rotate", wand_rotate, -1);
2289
+ rb_define_method(mwr_cWand, "sharpen", wand_sharpen, -1);
2290
+ rb_define_method(mwr_cWand, "signature", wand_signature, 0);
2291
+ rb_define_method(mwr_cWand, "strip", wand_strip, 0);
2292
+ rb_define_method(mwr_cWand, "type", wand_type, 0);
2293
+ rb_define_method(mwr_cWand, "write_string", wand_write_string, -1);
2294
+ rb_define_method(mwr_cWand, "write", wand_write, -1);
2295
+
2296
+ }