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.
- data/LICENSE +20 -0
- data/README +2 -0
- data/Rakefile +28 -0
- data/ext/magickwand/extconf.rb +81 -0
- data/ext/magickwand/gc.c +464 -0
- data/ext/magickwand/magickwand.c +152 -0
- data/ext/magickwand/magickwand.h +91 -0
- data/ext/magickwand/utility.c +819 -0
- data/ext/magickwand/wand.c +2296 -0
- data/lib/magickwand.rb +28 -0
- data/test/Button_0.gif +0 -0
- data/test/Button_1.gif +0 -0
- data/test/Button_2.gif +0 -0
- data/test/Button_3.gif +0 -0
- data/test/Button_4.gif +0 -0
- data/test/Button_5.gif +0 -0
- data/test/Button_6.gif +0 -0
- data/test/Button_7.gif +0 -0
- data/test/Button_8.gif +0 -0
- data/test/Button_9.gif +0 -0
- data/test/bridges.jpg +0 -0
- data/test/test_magickwand.rb +1100 -0
- metadata +78 -0
@@ -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
|
+
}
|