magickwand 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }