rmagick 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rmagick might be problematic. Click here for more details.

Files changed (88) hide show
  1. data/ChangeLog +10 -2
  2. data/README.html +7 -7
  3. data/doc/comtasks.html +2 -2
  4. data/doc/constants.html +5 -5
  5. data/doc/draw.html +131 -20
  6. data/doc/ex/InitialCoords.rb +1 -1
  7. data/doc/ex/NewCoordSys.rb +2 -2
  8. data/doc/ex/OrigCoordSys.rb +1 -1
  9. data/doc/ex/RotateScale.rb +2 -2
  10. data/doc/ex/Skew.rb +2 -2
  11. data/doc/ex/ViewBox.rb +1 -1
  12. data/doc/ex/arc.rb +4 -2
  13. data/doc/ex/arcs02.rb +1 -1
  14. data/doc/ex/bounding_box.rb +3 -0
  15. data/doc/ex/cbezier1.rb +2 -0
  16. data/doc/ex/cbezier2.rb +2 -0
  17. data/doc/ex/cbezier3.rb +2 -0
  18. data/doc/ex/cbezier4.rb +2 -0
  19. data/doc/ex/cbezier5.rb +2 -0
  20. data/doc/ex/cbezier6.rb +2 -0
  21. data/doc/ex/channel.rb +1 -0
  22. data/doc/ex/circle.rb +2 -0
  23. data/doc/ex/cubic01.rb +1 -1
  24. data/doc/ex/cubic02.rb +1 -1
  25. data/doc/ex/ellipse.rb +2 -0
  26. data/doc/ex/font_styles.rb +12 -10
  27. data/doc/ex/get_type_metrics.rb +2 -0
  28. data/doc/ex/grav.rb +2 -0
  29. data/doc/ex/image.rb +1 -1
  30. data/doc/ex/line.rb +2 -0
  31. data/doc/ex/opacity.rb +6 -4
  32. data/doc/ex/path.rb +2 -0
  33. data/doc/ex/polaroid.rb +2 -0
  34. data/doc/ex/qbezierpath.rb +3 -0
  35. data/doc/ex/rectangle.rb +2 -0
  36. data/doc/ex/rotate.rb +2 -0
  37. data/doc/ex/roundrect.rb +2 -0
  38. data/doc/ex/skewx.rb +2 -0
  39. data/doc/ex/skewy.rb +2 -0
  40. data/doc/ex/stroke_dasharray.rb +2 -0
  41. data/doc/ex/stroke_width.rb +2 -0
  42. data/doc/ex/text.rb +3 -2
  43. data/doc/ex/text01.rb +1 -1
  44. data/doc/ex/text_styles.rb +1 -1
  45. data/doc/ex/text_undercolor.rb +2 -0
  46. data/doc/ex/translate.rb +3 -1
  47. data/doc/ilist.html +2 -2
  48. data/doc/image1.html +2 -2
  49. data/doc/image2.html +2 -2
  50. data/doc/image3.html +37 -6
  51. data/doc/imageattrs.html +2 -2
  52. data/doc/imusage.html +3 -3
  53. data/doc/index.html +3 -3
  54. data/doc/info.html +2 -2
  55. data/doc/magick.html +2 -2
  56. data/doc/optequiv.html +24 -2
  57. data/doc/rvg.html +2 -2
  58. data/doc/rvgclip.html +2 -2
  59. data/doc/rvggroup.html +2 -2
  60. data/doc/rvgimage.html +2 -2
  61. data/doc/rvgpattern.html +2 -2
  62. data/doc/rvgshape.html +2 -2
  63. data/doc/rvgstyle.html +2 -2
  64. data/doc/rvgtext.html +2 -2
  65. data/doc/rvgtspan.html +2 -2
  66. data/doc/rvgtut.html +2 -2
  67. data/doc/rvguse.html +2 -2
  68. data/doc/rvgxform.html +2 -2
  69. data/doc/struct.html +2 -2
  70. data/doc/usage.html +6 -4
  71. data/ext/RMagick/MANIFEST +6 -1
  72. data/ext/RMagick/extconf.rb +18 -2
  73. data/ext/RMagick/rmagick.c +312 -0
  74. data/ext/RMagick/rmagick.h +137 -61
  75. data/ext/RMagick/rmdraw.c +271 -377
  76. data/ext/RMagick/rmenum.c +1016 -0
  77. data/ext/RMagick/rmimage.c +172 -16
  78. data/ext/RMagick/rminfo.c +5 -5
  79. data/ext/RMagick/rmmain.c +50 -303
  80. data/ext/RMagick/rmmontage.c +386 -0
  81. data/ext/RMagick/rmpixel.c +816 -0
  82. data/ext/RMagick/rmstruct.c +887 -0
  83. data/ext/RMagick/rmutil.c +25 -2634
  84. data/lib/RMagick.rb +46 -2
  85. data/lib/rvg/misc.rb +5 -4
  86. data/lib/rvg/stylable.rb +7 -1
  87. data/rmagick.gemspec +1 -1
  88. metadata +7 -2
@@ -0,0 +1,386 @@
1
+ /* $Id: rmmontage.c,v 1.3 2009/01/01 23:22:45 rmagick Exp $ */
2
+ /*============================================================================\
3
+ | Copyright (C) 2008 by Timothy P. Hunter
4
+ | Name: rmdraw.c
5
+ | Author: Tim Hunter
6
+ | Purpose: Contains Montage class methods.
7
+ \============================================================================*/
8
+
9
+ #include "rmagick.h"
10
+
11
+
12
+
13
+
14
+
15
+ /*
16
+ Static: destroy_Montage
17
+ Purpose: destory the MontageInfo struct and free the Montage struct
18
+ Notes: if the Magick::Montage#texture method wrote a texture file,
19
+ the file is deleted here.
20
+ */
21
+ static void
22
+ destroy_Montage(void *obj)
23
+ {
24
+ Montage *montage = obj;
25
+
26
+ // If we saved a temporary texture image, delete it now.
27
+ if (montage->info && montage->info->texture != NULL)
28
+ {
29
+ rm_delete_temp_image(montage->info->texture);
30
+ magick_free(montage->info->texture);
31
+ montage->info->texture = NULL;
32
+ }
33
+ if (montage->info)
34
+ {
35
+ (void) DestroyMontageInfo(montage->info);
36
+ montage->info = NULL;
37
+ }
38
+ xfree(montage);
39
+ }
40
+
41
+
42
+ /*
43
+ Method: Montage.new
44
+ Purpose: Create a new Montage object
45
+ */
46
+ VALUE
47
+ Montage_alloc(VALUE class)
48
+ {
49
+ MontageInfo *montage_info;
50
+ Montage *montage;
51
+ Info *image_info;
52
+ volatile VALUE montage_obj;
53
+
54
+ // DO NOT call rm_info_new - we don't want to support an Info parm block.
55
+ image_info = CloneImageInfo(NULL);
56
+ if (!image_info)
57
+ {
58
+ rb_raise(rb_eNoMemError, "not enough memory to initialize Info object");
59
+ }
60
+
61
+ montage_info = CloneMontageInfo(image_info, NULL);
62
+ (void) (void) DestroyImageInfo(image_info);
63
+
64
+ if (!montage_info)
65
+ {
66
+ rb_raise(rb_eNoMemError, "not enough memory to initialize Magick::Montage object");
67
+ }
68
+
69
+ montage = ALLOC(Montage);
70
+ montage->info = montage_info;
71
+ montage->compose = OverCompositeOp;
72
+ montage_obj = Data_Wrap_Struct(class, NULL, destroy_Montage, montage);
73
+
74
+ return montage_obj;
75
+ }
76
+
77
+
78
+ /*
79
+ Method: Magick::Montage#background_color(color-name)
80
+ Purpose: set background_color value
81
+ */
82
+ VALUE
83
+ Montage_background_color_eq(VALUE self, VALUE color)
84
+ {
85
+ Montage *montage;
86
+
87
+ Data_Get_Struct(self, Montage, montage);
88
+ Color_to_PixelPacket(&montage->info->background_color, color);
89
+ return self;
90
+ }
91
+
92
+
93
+ /*
94
+ Method: Magick::Montage#border_color(color-name)
95
+ Purpose: set border_color value
96
+ */
97
+ VALUE
98
+ Montage_border_color_eq(VALUE self, VALUE color)
99
+ {
100
+ Montage *montage;
101
+
102
+ Data_Get_Struct(self, Montage, montage);
103
+ Color_to_PixelPacket(&montage->info->border_color, color);
104
+ return self;
105
+ }
106
+
107
+
108
+ /*
109
+ Method: Magick::Montage#border_width(width)
110
+ Purpose: set border_width value
111
+ */
112
+ VALUE
113
+ Montage_border_width_eq(VALUE self, VALUE width)
114
+ {
115
+ Montage *montage;
116
+
117
+ Data_Get_Struct(self, Montage, montage);
118
+ montage->info->border_width = NUM2ULONG(width);
119
+ return self;
120
+ }
121
+
122
+
123
+ /*
124
+ Method: Magick::Montage#compose(width)
125
+ Purpose: set a composition operator
126
+ */
127
+ VALUE
128
+ Montage_compose_eq(VALUE self, VALUE compose)
129
+ {
130
+ Montage *montage;
131
+
132
+ Data_Get_Struct(self, Montage, montage);
133
+ VALUE_TO_ENUM(compose, montage->compose, CompositeOperator);
134
+ return self;
135
+ }
136
+
137
+
138
+ /*
139
+ Method: Magick::Montage#filename(name)
140
+ Purpose: set filename value
141
+ */
142
+ VALUE
143
+ Montage_filename_eq(VALUE self, VALUE filename)
144
+ {
145
+ Montage *montage;
146
+
147
+ Data_Get_Struct(self, Montage, montage);
148
+ strncpy(montage->info->filename, StringValuePtr(filename), MaxTextExtent-1);
149
+ return self;
150
+ }
151
+
152
+
153
+ /*
154
+ Method: Magick::Montage#fill(color-name)
155
+ Purpose: set fill value
156
+ */
157
+ VALUE
158
+ Montage_fill_eq(VALUE self, VALUE color)
159
+ {
160
+ Montage *montage;
161
+
162
+ Data_Get_Struct(self, Montage, montage);
163
+ Color_to_PixelPacket(&montage->info->fill, color);
164
+ return self;
165
+ }
166
+
167
+
168
+ /*
169
+ Method: Magick::Montage#font(font-name)
170
+ Purpose: set font value
171
+ */
172
+ VALUE
173
+ Montage_font_eq(VALUE self, VALUE font)
174
+ {
175
+ Montage *montage;
176
+
177
+ Data_Get_Struct(self, Montage, montage);
178
+ magick_clone_string(&montage->info->font, StringValuePtr(font));
179
+
180
+ return self;
181
+ }
182
+
183
+
184
+ /*
185
+ Method: Magick::Montage#frame(frame-geometry)
186
+ Purpose: set frame value
187
+ Notes: The geometry is a string in the form:
188
+ <width>x<height>+<outer-bevel-width>+<inner-bevel-width>
189
+ or a Geometry object
190
+ */
191
+ VALUE
192
+ Montage_frame_eq(VALUE self, VALUE frame_arg)
193
+ {
194
+ Montage *montage;
195
+ volatile VALUE frame;
196
+
197
+ Data_Get_Struct(self, Montage, montage);
198
+ frame = rm_to_s(frame_arg);
199
+ magick_clone_string(&montage->info->frame, StringValuePtr(frame));
200
+
201
+ return self;
202
+ }
203
+
204
+
205
+ /*
206
+ Method: Magick::Montage#geometry(geometry)
207
+ Purpose: set geometry value
208
+ */
209
+ VALUE
210
+ Montage_geometry_eq(VALUE self, VALUE geometry_arg)
211
+ {
212
+ Montage *montage;
213
+ volatile VALUE geometry;
214
+
215
+ Data_Get_Struct(self, Montage, montage);
216
+ geometry = rm_to_s(geometry_arg);
217
+ magick_clone_string(&montage->info->geometry, StringValuePtr(geometry));
218
+
219
+ return self;
220
+ }
221
+
222
+
223
+ /*
224
+ Method: Magick::Montage#gravity(gravity-type)
225
+ Purpose: set gravity value
226
+ */
227
+ VALUE
228
+ Montage_gravity_eq(VALUE self, VALUE gravity)
229
+ {
230
+ Montage *montage;
231
+
232
+ Data_Get_Struct(self, Montage, montage);
233
+ VALUE_TO_ENUM(gravity, montage->info->gravity, GravityType);
234
+ return self;
235
+ }
236
+
237
+
238
+ /*
239
+ Method: Magick::Montage#initialize
240
+ Purpose: Place-holder
241
+ */
242
+ VALUE
243
+ Montage_initialize(VALUE self)
244
+ {
245
+ // Nothing to do!
246
+ return self;
247
+ }
248
+
249
+
250
+ /*
251
+ Method: Magick::Montage#matte_color(color-name)
252
+ Purpose: set matte_color value
253
+ */
254
+ VALUE
255
+ Montage_matte_color_eq(VALUE self, VALUE color)
256
+ {
257
+ Montage *montage;
258
+
259
+ Data_Get_Struct(self, Montage, montage);
260
+ Color_to_PixelPacket(&montage->info->matte_color, color);
261
+ return self;
262
+ }
263
+
264
+
265
+ /*
266
+ Method: Magick::Montage#pointsize=size
267
+ Purpose: set pointsize value
268
+ */
269
+ VALUE
270
+ Montage_pointsize_eq(VALUE self, VALUE size)
271
+ {
272
+ Montage *montage;
273
+
274
+ Data_Get_Struct(self, Montage, montage);
275
+ montage->info->pointsize = NUM2DBL(size);
276
+ return self;
277
+ }
278
+
279
+
280
+ /*
281
+ Method: Magick::Montage#shadow=shadow
282
+ Purpose: set shadow value
283
+ */
284
+ VALUE
285
+ Montage_shadow_eq(VALUE self, VALUE shadow)
286
+ {
287
+ Montage *montage;
288
+
289
+ Data_Get_Struct(self, Montage, montage);
290
+ montage->info->shadow = (MagickBooleanType) RTEST(shadow);
291
+ return self;
292
+ }
293
+
294
+
295
+ /*
296
+ Method: Magick::Montage#stroke(color-name)
297
+ Purpose: set stroke value
298
+ */
299
+ VALUE
300
+ Montage_stroke_eq(VALUE self, VALUE color)
301
+ {
302
+ Montage *montage;
303
+
304
+ Data_Get_Struct(self, Montage, montage);
305
+ Color_to_PixelPacket(&montage->info->stroke, color);
306
+ return self;
307
+ }
308
+
309
+
310
+ /*
311
+ Method: Montage#texture(texture-image)
312
+ Purpose: set texture value
313
+ */
314
+ VALUE
315
+ Montage_texture_eq(VALUE self, VALUE texture)
316
+ {
317
+ Montage *montage;
318
+ Image *texture_image;
319
+ char temp_name[MaxTextExtent];
320
+
321
+ Data_Get_Struct(self, Montage, montage);
322
+
323
+ // If we had a previously defined temp texture image,
324
+ // remove it now in preparation for this new one.
325
+ if (montage->info->texture)
326
+ {
327
+ rm_delete_temp_image(montage->info->texture);
328
+ magick_free(montage->info->texture);
329
+ montage->info->texture = NULL;
330
+ }
331
+
332
+ texture = rm_cur_image(texture);
333
+ texture_image = rm_check_destroyed(texture);
334
+
335
+ // Write a temp copy of the image & save its name.
336
+ rm_write_temp_image(texture_image, temp_name);
337
+ magick_clone_string(&montage->info->texture, temp_name);
338
+
339
+ return self;
340
+ }
341
+
342
+
343
+ /*
344
+ Method: Magick::Montage#tile(tile)
345
+ Purpose: set tile value
346
+ */
347
+ VALUE
348
+ Montage_tile_eq(VALUE self, VALUE tile_arg)
349
+ {
350
+ Montage *montage;
351
+ volatile VALUE tile;
352
+
353
+ Data_Get_Struct(self, Montage, montage);
354
+ tile = rm_to_s(tile_arg);
355
+ magick_clone_string(&montage->info->tile, StringValuePtr(tile));
356
+
357
+ return self;
358
+ }
359
+
360
+
361
+ /*
362
+ Method: Magick::Montage#title(title)
363
+ Purpose: set title value
364
+ */
365
+ VALUE
366
+ Montage_title_eq(VALUE self, VALUE title)
367
+ {
368
+ Montage *montage;
369
+
370
+ Data_Get_Struct(self, Montage, montage);
371
+ magick_clone_string(&montage->info->title, StringValuePtr(title));
372
+ return self;
373
+ }
374
+
375
+
376
+ /*
377
+ Extern: rm_montage_new()
378
+ Purpose: Return a new Magick::Montage object
379
+ */
380
+
381
+ VALUE
382
+ rm_montage_new(void)
383
+ {
384
+ return Montage_initialize(Montage_alloc(Class_Montage));
385
+ }
386
+
@@ -0,0 +1,816 @@
1
+ /* $Id: rmpixel.c,v 1.2 2008/12/27 00:15:56 rmagick Exp $ */
2
+ /*============================================================================\
3
+ | Copyright (C) 2008 by Timothy P. Hunter
4
+ | Name: rmpixel.c
5
+ | Author: Tim Hunter
6
+ | Purpose: Contains Pixel class methods.
7
+ \============================================================================*/
8
+
9
+ #include "rmagick.h"
10
+
11
+
12
+
13
+
14
+ static void Color_Name_to_PixelPacket(PixelPacket *, VALUE);
15
+
16
+
17
+
18
+
19
+ /*
20
+ Extern: destroy_Pixel
21
+ Purpose: Free the storage associated with a Pixel object
22
+ */
23
+ void
24
+ destroy_Pixel(Pixel *pixel)
25
+ {
26
+ xfree(pixel);
27
+ }
28
+
29
+
30
+ /*
31
+ Methods: Pixel RGBA attribute accessors
32
+ Purpose: Get/set Pixel attributes
33
+ Note: Pixel is Observable. Setters call changed, notify_observers
34
+ Note: Setters return their argument values for backward compatibility
35
+ to when Pixel was a Struct class.
36
+ */
37
+ DEF_ATTR_READER(Pixel, red, int)
38
+ DEF_ATTR_READER(Pixel, green, int)
39
+ DEF_ATTR_READER(Pixel, blue, int)
40
+ DEF_ATTR_READER(Pixel, opacity, int)
41
+ DEF_PIXEL_CHANNEL_WRITER(red)
42
+ DEF_PIXEL_CHANNEL_WRITER(green)
43
+ DEF_PIXEL_CHANNEL_WRITER(blue)
44
+ DEF_PIXEL_CHANNEL_WRITER(opacity)
45
+
46
+
47
+ /*
48
+ Methods: Pixel CMYK attribute accessors
49
+ Purpose: Get/set Pixel attributes
50
+ Note: Pixel is Observable. Setters call changed, notify_observers
51
+ Note: Setters return their argument values for backward compatibility
52
+ to when Pixel was a Struct class.
53
+ */
54
+ DEF_PIXEL_CMYK_CHANNEL_ACCESSOR(cyan, red)
55
+ DEF_PIXEL_CMYK_CHANNEL_ACCESSOR(magenta, green)
56
+ DEF_PIXEL_CMYK_CHANNEL_ACCESSOR(yellow, blue)
57
+ DEF_PIXEL_CMYK_CHANNEL_ACCESSOR(black, opacity)
58
+
59
+
60
+ /*
61
+ * Static: color_arg_rescue
62
+ * Purpose: raise ArgumentError if the color name cannot be converted
63
+ * to a string via rb_str_to_str.
64
+ */
65
+ static VALUE
66
+ color_arg_rescue(VALUE arg)
67
+ {
68
+ rb_raise(rb_eTypeError, "argument must be color name or pixel (%s given)",
69
+ rb_class2name(CLASS_OF(arg)));
70
+ return (VALUE)0;
71
+ }
72
+
73
+
74
+ /*
75
+ Extern: Color_to_PixelPacket
76
+ Purpose: Convert either a String color name or
77
+ a Magick::Pixel to a PixelPacket
78
+ */
79
+ void
80
+ Color_to_PixelPacket(PixelPacket *pp, VALUE color)
81
+ {
82
+ Pixel *pixel;
83
+
84
+ // Allow color name or Pixel
85
+ if (CLASS_OF(color) == Class_Pixel)
86
+ {
87
+ Data_Get_Struct(color, Pixel, pixel);
88
+ *pp = *pixel;
89
+ }
90
+ else
91
+ {
92
+ // require 'to_str' here instead of just 'to_s'.
93
+ color = rb_rescue(rb_str_to_str, color, color_arg_rescue, color);
94
+ Color_Name_to_PixelPacket(pp, color);
95
+ }
96
+ }
97
+
98
+
99
+ /*
100
+ Static: Color_Name_to_PixelPacket
101
+ Purpose: Convert a color name to a PixelPacket
102
+ Raises: ArgumentError
103
+ */
104
+ static void
105
+ Color_Name_to_PixelPacket(PixelPacket *color, VALUE name_arg)
106
+ {
107
+ MagickBooleanType okay;
108
+ char *name;
109
+ ExceptionInfo exception;
110
+
111
+ GetExceptionInfo(&exception);
112
+ name = StringValuePtr(name_arg);
113
+ okay = QueryColorDatabase(name, color, &exception);
114
+ (void) DestroyExceptionInfo(&exception);
115
+ if (!okay)
116
+ {
117
+ rb_raise(rb_eArgError, "invalid color name %s", name);
118
+ }
119
+ }
120
+
121
+
122
+
123
+ /*
124
+ Extern: Pixel_alloc
125
+ Purpose: Allocate a Pixel object
126
+ */
127
+ VALUE
128
+ Pixel_alloc(VALUE class)
129
+ {
130
+ Pixel *pixel;
131
+
132
+ pixel = ALLOC(Pixel);
133
+ memset(pixel, '\0', sizeof(Pixel));
134
+ return Data_Wrap_Struct(class, NULL, destroy_Pixel, pixel);
135
+ }
136
+
137
+
138
+ /*
139
+ Method: Pixel#===
140
+ Purpose: "Case equal" operator for Pixel
141
+ */
142
+
143
+ VALUE
144
+ Pixel_case_eq(VALUE self, VALUE other)
145
+ {
146
+ Pixel *this, *that;
147
+
148
+ if (CLASS_OF(self) == CLASS_OF(other))
149
+ {
150
+ Data_Get_Struct(self, Pixel, this);
151
+ Data_Get_Struct(other, Pixel, that);
152
+ return (this->red == that->red
153
+ && this->blue == that->blue
154
+ && this->green == that->green
155
+ && this->opacity == that->opacity) ? Qtrue : Qfalse;
156
+ }
157
+
158
+ return Qfalse;
159
+ }
160
+
161
+
162
+ /*
163
+ Method: Pixel#clone
164
+ Notes: see dup, init_copy
165
+ */
166
+ VALUE
167
+ Pixel_clone(VALUE self)
168
+ {
169
+ volatile VALUE clone;
170
+
171
+ clone = Pixel_dup(self);
172
+ if (OBJ_FROZEN(self))
173
+ {
174
+ OBJ_FREEZE(clone);
175
+ }
176
+
177
+ return clone;
178
+ }
179
+
180
+
181
+ VALUE
182
+ Pixel_dup(VALUE self)
183
+ {
184
+ Pixel *pixel;
185
+ volatile VALUE dup;
186
+
187
+ pixel = ALLOC(Pixel);
188
+ memset(pixel, '\0', sizeof(Pixel));
189
+ dup = Data_Wrap_Struct(CLASS_OF(self), NULL, destroy_Pixel, pixel);
190
+ if (rb_obj_tainted(self))
191
+ {
192
+ (void) rb_obj_taint(dup);
193
+ }
194
+ return rb_funcall(dup, rm_ID_initialize_copy, 1, self);
195
+ }
196
+
197
+
198
+ /*
199
+ Method: Pixel#eql?
200
+ Purpose: For use with Hash
201
+ */
202
+ VALUE
203
+ Pixel_eql_q(VALUE self, VALUE other)
204
+ {
205
+ return NUM2INT(Pixel_spaceship(self, other)) == 0 ? Qtrue : Qfalse;
206
+ }
207
+
208
+
209
+ /*
210
+ Method: Pixel#fcmp(other[, fuzz[, colorspace]])
211
+ Purpose: Compare pixel values for equality
212
+ */
213
+ VALUE
214
+ Pixel_fcmp(int argc, VALUE *argv, VALUE self)
215
+ {
216
+ Image *image;
217
+ Info *info;
218
+
219
+ Pixel *this, *that;
220
+ ColorspaceType colorspace = RGBColorspace;
221
+ double fuzz = 0.0;
222
+ unsigned int equal;
223
+
224
+ switch (argc)
225
+ {
226
+ case 3:
227
+ VALUE_TO_ENUM(argv[2], colorspace, ColorspaceType);
228
+ case 2:
229
+ fuzz = NUM2DBL(argv[1]);
230
+ case 1:
231
+ // Allow 1 argument
232
+ break;
233
+ default:
234
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to 3)", argc);
235
+ break;
236
+ }
237
+
238
+ Data_Get_Struct(self, Pixel, this);
239
+ Data_Get_Struct(argv[0], Pixel, that);
240
+
241
+ // The IsColorSimilar function expects to get the
242
+ // colorspace and fuzz parameters from an Image structure.
243
+
244
+ info = CloneImageInfo(NULL);
245
+ if (!info)
246
+ {
247
+ rb_raise(rb_eNoMemError, "not enough memory to continue");
248
+ }
249
+
250
+ image = AcquireImage(info);
251
+
252
+ // Delete Info now in case we have to raise an exception
253
+ (void) DestroyImageInfo(info);
254
+
255
+ if (!image)
256
+ {
257
+ rb_raise(rb_eNoMemError, "not enough memory to continue");
258
+ }
259
+
260
+ image->colorspace = colorspace;
261
+ image->fuzz = fuzz;
262
+
263
+ equal = IsColorSimilar(image, this, that);
264
+ (void) DestroyImage(image);
265
+
266
+ return equal ? Qtrue : Qfalse;
267
+ }
268
+
269
+
270
+ /*
271
+ Method: Magick::Pixel.from_color(string)
272
+ Purpose: Construct an Magick::Pixel corresponding to the given color name.
273
+ Notes: the "inverse" is Image *#to_color, b/c the conversion of a pixel
274
+ to a color name requires both a color depth and if the opacity
275
+ value has meaning (i.e. whether image->matte == True or not).
276
+
277
+ Also see Magick::Pixel#to_color, below.
278
+ */
279
+ VALUE
280
+ Pixel_from_color(VALUE class, VALUE name)
281
+ {
282
+ PixelPacket pp;
283
+ ExceptionInfo exception;
284
+ MagickBooleanType okay;
285
+
286
+ class = class; // defeat "never referenced" message from icc
287
+
288
+ GetExceptionInfo(&exception);
289
+ okay = QueryColorDatabase(StringValuePtr(name), &pp, &exception);
290
+ CHECK_EXCEPTION()
291
+ (void) DestroyExceptionInfo(&exception);
292
+
293
+ if (!okay)
294
+ {
295
+ rb_raise(rb_eArgError, "invalid color name: %s", StringValuePtr(name));
296
+ }
297
+
298
+ return Pixel_from_PixelPacket(&pp);
299
+ }
300
+
301
+
302
+ /*
303
+ Method: Pixel#from_hsla(hue, saturation, lightness, alpha=1)
304
+ Purpose: Replace brain-dead from_HSL, above.
305
+ Notes: 0 <= hue < 360, 0 <= saturation <= 1, 0 <= lightness <= 1
306
+ 0 <= alpha <= 1 (0 is transparent, 1 is opaque)
307
+ */
308
+ VALUE
309
+ Pixel_from_hsla(int argc, VALUE *argv, VALUE class)
310
+ {
311
+ double h, s, l, a = 1.0;
312
+ MagickPixelPacket pp;
313
+ ExceptionInfo exception;
314
+ char name[50];
315
+ MagickBooleanType alpha = MagickFalse;
316
+
317
+ class = class; // defeat "unused parameter" message.
318
+
319
+ switch (argc)
320
+ {
321
+ case 4:
322
+ a = NUM2DBL(argv[3]);
323
+ alpha = MagickTrue;
324
+ case 3:
325
+ l = NUM2DBL(argv[2]);
326
+ s = NUM2DBL(argv[1]);
327
+ h = NUM2DBL(argv[0]);
328
+ break;
329
+ default:
330
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 3 or 4)", argc);
331
+ break;
332
+ }
333
+
334
+ if (alpha && (a < 0.0 || a > 1.0))
335
+ {
336
+ rb_raise(rb_eRangeError, "alpha %g out of range [0.0, 1.0]", a);
337
+ }
338
+ if (l < 0.0 || l > 100.0)
339
+ {
340
+ rb_raise(rb_eRangeError, "lightness %g out of range [0.0, 100.0]", l);
341
+ }
342
+ if (s < 0.0 || s > 100.0)
343
+ {
344
+ rb_raise(rb_eRangeError, "saturation %g out of range [0.0, 100.0]", s);
345
+ }
346
+ if (h < 0.0 || h >= 360.0)
347
+ {
348
+ rb_raise(rb_eRangeError, "hue %g out of range [0.0, 360.0)", h);
349
+ }
350
+
351
+ memset(name, 0, sizeof(name));
352
+ if (alpha)
353
+ {
354
+ sprintf(name, "hsla(%-2.1f,%-2.1f,%-2.1f,%-2.1f)", h, s, l, a);
355
+ }
356
+ else
357
+ {
358
+ sprintf(name, "hsl(%-2.1f,%-2.1f,%-2.1f)", h, s, l);
359
+ }
360
+
361
+ GetExceptionInfo(&exception);
362
+
363
+ (void) QueryMagickColor(name, &pp, &exception);
364
+ CHECK_EXCEPTION()
365
+
366
+ (void) DestroyExceptionInfo(&exception);
367
+
368
+ return Pixel_from_MagickPixelPacket(&pp);
369
+ }
370
+
371
+
372
+ /*
373
+ Method: Pixel.from_HSL *** DEPRECATED ***
374
+ Purpose: Constructs an RGB pixel from the array
375
+ [hue, saturation, luminosity].
376
+ */
377
+ VALUE
378
+ Pixel_from_HSL(VALUE class, VALUE hsl)
379
+ {
380
+ PixelPacket rgb;
381
+ double hue, saturation, luminosity;
382
+
383
+ class = class; // defeat "never referenced" message from icc
384
+ memset(&rgb, 0, sizeof(rgb));
385
+
386
+ hsl = rb_Array(hsl); // Ensure array
387
+ if (RARRAY_LEN(hsl) < 3)
388
+ {
389
+ rb_raise(rb_eArgError, "array argument must have at least 3 elements");
390
+ }
391
+
392
+ hue = NUM2DBL(rb_ary_entry(hsl, 0));
393
+ saturation = NUM2DBL(rb_ary_entry(hsl, 1));
394
+ luminosity = NUM2DBL(rb_ary_entry(hsl, 2));
395
+
396
+ #if defined(HAVE_CONVERTHSLTORGB)
397
+ rb_warning("Pixel#from_HSL is deprecated; use from_hsla");
398
+ ConvertHSLToRGB(hue, saturation, luminosity,
399
+ &rgb.red, &rgb.green, &rgb.blue);
400
+ #else
401
+ HSLTransform(hue, saturation, luminosity,
402
+ &rgb.red, &rgb.green, &rgb.blue);
403
+ #endif
404
+ return Pixel_from_PixelPacket(&rgb);
405
+ }
406
+
407
+
408
+ /*
409
+ Static: Pixel_from_MagickPixelPacket
410
+ Purpose: Create a Magick::Pixel object from a MagickPixelPacket structure.
411
+ Notes: bypasses normal Pixel.new, Pixel#initialize methods
412
+ */
413
+ VALUE
414
+ Pixel_from_MagickPixelPacket(const MagickPixelPacket *pp)
415
+ {
416
+ Pixel *pixel;
417
+
418
+ pixel = ALLOC(Pixel);
419
+ pixel->red = ROUND_TO_QUANTUM(pp->red);
420
+ pixel->green = ROUND_TO_QUANTUM(pp->green);
421
+ pixel->blue = ROUND_TO_QUANTUM(pp->blue);
422
+ pixel->opacity = ROUND_TO_QUANTUM(pp->opacity);
423
+
424
+ return Data_Wrap_Struct(Class_Pixel, NULL, destroy_Pixel, pixel);
425
+ }
426
+
427
+
428
+ /*
429
+ Extern: Pixel_from_PixelPacket
430
+ Purpose: Create a Magick::Pixel object from a PixelPacket structure.
431
+ Notes: bypasses normal Pixel.new, Pixel#initialize methods
432
+ */
433
+ VALUE
434
+ Pixel_from_PixelPacket(const PixelPacket *pp)
435
+ {
436
+ Pixel *pixel;
437
+
438
+ pixel = ALLOC(Pixel);
439
+ *pixel = *pp;
440
+ return Data_Wrap_Struct(Class_Pixel, NULL, destroy_Pixel, pixel);
441
+ }
442
+
443
+
444
+ /*
445
+ Method: Pixel#hash
446
+ Notes: INT2FIX left-shifts 1 bit. Sacrifice 1 bit
447
+ from the opacity attribute to the FIXNUM_FLAG.
448
+ */
449
+ VALUE
450
+ Pixel_hash(VALUE self)
451
+ {
452
+ Pixel *pixel;
453
+ unsigned int hash;
454
+
455
+ Data_Get_Struct(self, Pixel, pixel);
456
+
457
+ hash = ScaleQuantumToChar(pixel->red) << 24;
458
+ hash += ScaleQuantumToChar(pixel->green) << 16;
459
+ hash += ScaleQuantumToChar(pixel->blue) << 8;
460
+ hash += ScaleQuantumToChar(pixel->opacity);
461
+ hash >>= 1;
462
+
463
+ return INT2FIX(hash);
464
+
465
+ }
466
+
467
+
468
+ /*
469
+ Method: Pixel#initialize_copy
470
+ Purpose: initialize clone, dup methods
471
+ */
472
+ VALUE
473
+ Pixel_init_copy(VALUE self, VALUE orig)
474
+ {
475
+ Pixel *copy, *original;
476
+
477
+ Data_Get_Struct(orig, Pixel, original);
478
+ Data_Get_Struct(self, Pixel, copy);
479
+
480
+ *copy = *original;
481
+
482
+ return self;
483
+ }
484
+
485
+
486
+ /*
487
+ Method: Pixel#initialize(red=0,green=0,blue=0,opacity=0)
488
+ Notes: For backward compatibility, arguments may be nil.
489
+ */
490
+ VALUE
491
+ Pixel_initialize(int argc, VALUE *argv, VALUE self)
492
+ {
493
+ Pixel *pixel;
494
+
495
+ Data_Get_Struct(self, Pixel, pixel);
496
+
497
+ switch(argc)
498
+ {
499
+ case 4:
500
+ if (argv[3] != Qnil)
501
+ {
502
+ pixel->opacity = APP2QUANTUM(argv[3]);
503
+ }
504
+ case 3:
505
+ if (argv[2] != Qnil)
506
+ {
507
+ pixel->blue = APP2QUANTUM(argv[2]);
508
+ }
509
+ case 2:
510
+ if (argv[1] != Qnil)
511
+ {
512
+ pixel->green = APP2QUANTUM(argv[1]);
513
+ }
514
+ case 1:
515
+ if (argv[0] != Qnil)
516
+ {
517
+ pixel->red = APP2QUANTUM(argv[0]);
518
+ }
519
+ case 0:
520
+ break;
521
+ default:
522
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 4)", argc);
523
+ }
524
+
525
+ return self;
526
+ }
527
+
528
+
529
+ /*
530
+ Method: Pixel#intensity
531
+ Purpose: Return the "intensity" of a pixel
532
+ */
533
+ VALUE
534
+ Pixel_intensity(VALUE self)
535
+ {
536
+ Pixel *pixel;
537
+ Quantum intensity;
538
+
539
+ Data_Get_Struct(self, Pixel, pixel);
540
+
541
+ intensity = ROUND_TO_QUANTUM((0.299*pixel->red)
542
+ + (0.587*pixel->green)
543
+ + (0.114*pixel->blue));
544
+
545
+ return QUANTUM2NUM((unsigned long) intensity);
546
+ }
547
+
548
+
549
+ /*
550
+ Method: Pixel#marshal_dump
551
+ Purpose: Support Marshal.dump
552
+ */
553
+ VALUE
554
+ Pixel_marshal_dump(VALUE self)
555
+ {
556
+ Pixel *pixel;
557
+ volatile VALUE dpixel;
558
+
559
+ Data_Get_Struct(self, Pixel, pixel);
560
+ dpixel = rb_hash_new();
561
+ rb_hash_aset(dpixel, CSTR2SYM("red"), QUANTUM2NUM(pixel->red));
562
+ rb_hash_aset(dpixel, CSTR2SYM("green"), QUANTUM2NUM(pixel->green));
563
+ rb_hash_aset(dpixel, CSTR2SYM("blue"), QUANTUM2NUM(pixel->blue));
564
+ rb_hash_aset(dpixel, CSTR2SYM("opacity"), QUANTUM2NUM(pixel->opacity));
565
+ return dpixel;
566
+ }
567
+
568
+
569
+ /*
570
+ Method: Pixel#marshal_load
571
+ Purpose: Support Marshal.load
572
+ */
573
+ VALUE
574
+ Pixel_marshal_load(VALUE self, VALUE dpixel)
575
+ {
576
+ Pixel *pixel;
577
+
578
+ Data_Get_Struct(self, Pixel, pixel);
579
+ pixel->red = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("red")));
580
+ pixel->green = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("green")));
581
+ pixel->blue = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("blue")));
582
+ pixel->opacity = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("opacity")));
583
+ return self;
584
+ }
585
+
586
+
587
+ /*
588
+ Method: Pixel#<=>
589
+ Purpose: Support Comparable mixin
590
+ */
591
+ VALUE
592
+ Pixel_spaceship(VALUE self, VALUE other)
593
+ {
594
+ Pixel *this, *that;
595
+
596
+ Data_Get_Struct(self, Pixel, this);
597
+ Data_Get_Struct(other, Pixel, that);
598
+
599
+ if (this->red != that->red)
600
+ {
601
+ return INT2NUM((this->red - that->red)/abs(this->red - that->red));
602
+ }
603
+ else if(this->green != that->green)
604
+ {
605
+ return INT2NUM((this->green - that->green)/abs(this->green - that->green));
606
+ }
607
+ else if(this->blue != that->blue)
608
+ {
609
+ return INT2NUM((this->blue - that->blue)/abs(this->blue - that->blue));
610
+ }
611
+ else if(this->opacity != that->opacity)
612
+ {
613
+ return INT2NUM((this->opacity - that->opacity)/abs(this->opacity - that->opacity));
614
+ }
615
+
616
+ // Values are equal, check class.
617
+
618
+ return rb_funcall(CLASS_OF(self), rb_intern("<=>"), 1, CLASS_OF(other));
619
+
620
+ }
621
+
622
+
623
+ /*
624
+ Method: Pixel#to_hsla()
625
+ Purpose: Replace brain-dead to_HSL, above.
626
+ Notes: Returns [hue, saturation, lightness, alpha] in the same ranges as from_hsla()
627
+ */
628
+ VALUE
629
+ Pixel_to_hsla(VALUE self)
630
+ {
631
+ double hue, sat, lum, alpha;
632
+ Pixel *pixel;
633
+ volatile VALUE hsla;
634
+
635
+ Data_Get_Struct(self, Pixel, pixel);
636
+
637
+ #if defined(HAVE_CONVERTRGBTOHSL)
638
+ ConvertRGBToHSL(pixel->red, pixel->green, pixel->blue, &hue, &sat, &lum);
639
+ #else
640
+ TransformHSL(pixel->red, pixel->green, pixel->blue, &hue, &sat, &lum);
641
+ #endif
642
+ hue *= 360.0;
643
+ sat *= 100.0;
644
+ lum *= 100.0;
645
+
646
+ if (pixel->opacity == OpaqueOpacity)
647
+ {
648
+ alpha = 1.0;
649
+ }
650
+ else if (pixel->opacity == TransparentOpacity)
651
+ {
652
+ alpha = 0.0;
653
+ }
654
+ else
655
+ {
656
+ alpha = ROUND_TO_QUANTUM(QuantumRange - (pixel->opacity / QuantumRange));
657
+ }
658
+
659
+ hsla = rb_ary_new3(4, rb_float_new(hue), rb_float_new(sat), rb_float_new(lum), rb_float_new(alpha));
660
+ return hsla;
661
+ }
662
+
663
+ /*
664
+ Method: Pixel#to_HSL *** DEPRECATED ***
665
+ Purpose: Converts an RGB pixel to the array
666
+ [hue, saturation, luminosity].
667
+ */
668
+ VALUE
669
+ Pixel_to_HSL(VALUE self)
670
+ {
671
+ Pixel *pixel;
672
+ double hue, saturation, luminosity;
673
+ volatile VALUE hsl;
674
+
675
+ Data_Get_Struct(self, Pixel, pixel);
676
+ #if defined(HAVE_CONVERTRGBTOHSL)
677
+ rb_warning("Pixel#to_HSL is deprecated; use to_hsla");
678
+ ConvertRGBToHSL(pixel->red, pixel->green, pixel->blue, &hue, &saturation, &luminosity);
679
+ #else
680
+ TransformHSL(pixel->red, pixel->green, pixel->blue, &hue, &saturation, &luminosity);
681
+ #endif
682
+
683
+ hsl = rb_ary_new3(3, rb_float_new(hue), rb_float_new(saturation),
684
+ rb_float_new(luminosity));
685
+
686
+ return hsl;
687
+ }
688
+
689
+
690
+ /*
691
+ Method: Magick::Pixel#to_color(compliance=AllCompliance, matte=false,
692
+ depth=QuantumDepth, hex=false)
693
+ Purpose: return the color name corresponding to the pixel values
694
+ Notes: the conversion respects the value of the 'opacity' field
695
+ in the Pixel.
696
+ */
697
+ VALUE
698
+ Pixel_to_color(int argc, VALUE *argv, VALUE self)
699
+ {
700
+ Info *info;
701
+ Image *image;
702
+ Pixel *pixel;
703
+ MagickPixelPacket mpp;
704
+ MagickBooleanType hex = MagickFalse;
705
+ char name[MaxTextExtent];
706
+ ExceptionInfo exception;
707
+ ComplianceType compliance = AllCompliance;
708
+ unsigned int matte = MagickFalse;
709
+ unsigned int depth = QuantumDepth;
710
+
711
+ switch (argc)
712
+ {
713
+ case 4:
714
+ hex = RTEST(argv[3]);
715
+ case 3:
716
+ depth = NUM2UINT(argv[2]);
717
+
718
+ // Ensure depth is appropriate for the way xMagick was compiled.
719
+ switch (depth)
720
+ {
721
+ case 8:
722
+ #if QuantumDepth == 16 || QuantumDepth == 32
723
+ case 16:
724
+ #endif
725
+ #if QuantumDepth == 32
726
+ case 32:
727
+ #endif
728
+ break;
729
+ default:
730
+ rb_raise(rb_eArgError, "invalid depth (%d)", depth);
731
+ break;
732
+ }
733
+ case 2:
734
+ matte = RTEST(argv[1]);
735
+ case 1:
736
+ VALUE_TO_ENUM(argv[0], compliance, ComplianceType);
737
+ case 0:
738
+ break;
739
+ default:
740
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 2)", argc);
741
+ }
742
+
743
+ Data_Get_Struct(self, Pixel, pixel);
744
+
745
+ info = CloneImageInfo(NULL);
746
+ image = AcquireImage(info);
747
+ image->depth = depth;
748
+ image->matte = matte;
749
+ (void) DestroyImageInfo(info);
750
+
751
+ GetMagickPixelPacket(image, &mpp);
752
+ rm_set_magick_pixel_packet(pixel, NULL, &mpp);
753
+
754
+ GetExceptionInfo(&exception);
755
+
756
+ #if defined(HAVE_NEW_QUERYMAGICKCOLORNAME)
757
+ // Support for hex-format color names moved out of QueryMagickColorname
758
+ // in 6.4.1-9. The 'hex' argument was removed as well.
759
+ if (hex)
760
+ {
761
+ if (compliance == XPMCompliance)
762
+ {
763
+ mpp.matte = MagickFalse;
764
+ mpp.depth = (unsigned long) min(1.0 * image->depth, 16.0);
765
+ }
766
+ (void) GetColorTuple(&mpp, MagickTrue, name);
767
+ }
768
+ else
769
+ {
770
+ (void) QueryMagickColorname(image, &mpp, compliance, name, &exception);
771
+ }
772
+ #else
773
+ (void) QueryMagickColorname(image, &mpp, compliance, hex, name, &exception);
774
+ #endif
775
+ (void) DestroyImage(image);
776
+ CHECK_EXCEPTION()
777
+ (void) DestroyExceptionInfo(&exception);
778
+
779
+ // Always return a string, even if it's ""
780
+ return rb_str_new2(name);
781
+ }
782
+
783
+
784
+ /*
785
+ Method: Magick::Pixel#to_s
786
+ Purpose: Create a string representation of a Magick::Pixel
787
+ */
788
+ VALUE
789
+ Pixel_to_s(VALUE self)
790
+ {
791
+ Pixel *pixel;
792
+ char buff[100];
793
+
794
+ Data_Get_Struct(self, Pixel, pixel);
795
+ sprintf(buff, "red=" QuantumFormat ", green=" QuantumFormat ", blue=" QuantumFormat ", opacity=" QuantumFormat
796
+ , pixel->red, pixel->green, pixel->blue, pixel->opacity);
797
+ return rb_str_new2(buff);
798
+ }
799
+
800
+
801
+ /*
802
+ Static: rm_set_magick_pixel_packet
803
+ Purpose: Convert a PixelPacket to a MagickPixelPacket
804
+ Notes: Same code as the private function SetMagickPixelPacket
805
+ in ImageMagick.
806
+ */
807
+ void
808
+ rm_set_magick_pixel_packet(Pixel *pixel, IndexPacket *index_packet, MagickPixelPacket *pp)
809
+ {
810
+ pp->red = (MagickRealType) pixel->red;
811
+ pp->green = (MagickRealType) pixel->green;
812
+ pp->blue = (MagickRealType) pixel->blue;
813
+ pp->opacity = (MagickRealType) (pp->matte ? pixel->opacity : OpaqueOpacity);
814
+ pp->index = (MagickRealType) ((pp->colorspace == CMYKColorspace) && (index_packet ? *index_packet : 0));
815
+ }
816
+