ruby-vips8 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +10 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +15 -0
  6. data/Gemfile.lock +84 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +170 -0
  9. data/Rakefile +45 -0
  10. data/TODO +11 -0
  11. data/VERSION +1 -0
  12. data/example/annotate.rb +17 -0
  13. data/example/daltonize8.rb +75 -0
  14. data/example/example1.rb +84 -0
  15. data/example/example2.rb +31 -0
  16. data/example/example3.rb +19 -0
  17. data/example/example4.rb +18 -0
  18. data/example/example5.rb +31 -0
  19. data/example/trim8.rb +41 -0
  20. data/example/watermark.rb +44 -0
  21. data/example/wobble.rb +36 -0
  22. data/lib/vips8.rb +153 -0
  23. data/lib/vips8/access.rb +14 -0
  24. data/lib/vips8/align.rb +11 -0
  25. data/lib/vips8/angle.rb +12 -0
  26. data/lib/vips8/angle45.rb +16 -0
  27. data/lib/vips8/argument.rb +163 -0
  28. data/lib/vips8/bandformat.rb +20 -0
  29. data/lib/vips8/call.rb +302 -0
  30. data/lib/vips8/coding.rb +14 -0
  31. data/lib/vips8/demandstyle.rb +35 -0
  32. data/lib/vips8/direction.rb +11 -0
  33. data/lib/vips8/error.rb +30 -0
  34. data/lib/vips8/extend.rb +22 -0
  35. data/lib/vips8/foreignflags.rb +20 -0
  36. data/lib/vips8/image.rb +1383 -0
  37. data/lib/vips8/interpolate.rb +37 -0
  38. data/lib/vips8/interpretation.rb +28 -0
  39. data/lib/vips8/methods.rb +1807 -0
  40. data/lib/vips8/operation.rb +19 -0
  41. data/ruby-vips8.gemspec +112 -0
  42. data/spec/image_spec.rb +515 -0
  43. data/spec/samples/balloon.v +0 -0
  44. data/spec/samples/ghost.ppm +405 -0
  45. data/spec/samples/huge.jpg +0 -0
  46. data/spec/samples/icc.jpg +0 -0
  47. data/spec/samples/lcd.icc +0 -0
  48. data/spec/samples/lion.svg +154 -0
  49. data/spec/samples/sample.csv +7 -0
  50. data/spec/samples/sample.exr +0 -0
  51. data/spec/samples/wagon.jpg +0 -0
  52. data/spec/samples/wagon.v +0 -0
  53. data/spec/spec_helper.rb +49 -0
  54. data/spec/vips_spec.rb +74 -0
  55. metadata +198 -0
@@ -0,0 +1,14 @@
1
+ module Vips
2
+ # The type of access an operation has to supply.
3
+ #
4
+ # * `:random` means requests can come in any order.
5
+ #
6
+ # * `:sequential` means requests will be top-to-bottom, but with some
7
+ # amount of buffering behind the read point for small non-local
8
+ # accesses.
9
+ #
10
+ # * `:sequential_unbuffered` means requests will be strictly
11
+ # top-to-bottom with no read-behind. This can save some memory.
12
+ class Access
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Vips
2
+
3
+ # Various types of alignment. See {Vips::Image.join}, for example.
4
+ #
5
+ # * `:low` Align on the low coordinate edge
6
+ # * `:centre` Align on the centre
7
+ # * `:high` Align on the high coordinate edge
8
+
9
+ class Align
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Vips
2
+
3
+ # Various fixed 90 degree rotation angles. See {Vips::Image.rot}.
4
+ #
5
+ # * `:d0` no rotate
6
+ # * `:d90` 90 degrees clockwise
7
+ # * `:d180` 180 degrees
8
+ # * `:d270` 90 degrees anti-clockwise
9
+
10
+ class Angle
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Vips
2
+
3
+ # Various fixed 45 degree rotation angles. See {Vips::Image.rot45}.
4
+ #
5
+ # * `:d0` no rotate
6
+ # * `:d45` 45 degrees clockwise
7
+ # * `:d90` 90 degrees clockwise
8
+ # * `:d135` 135 degrees clockwise
9
+ # * `:d180` 180 degrees
10
+ # * `:d225` 135 degrees anti-clockwise
11
+ # * `:d270` 90 degrees anti-clockwise
12
+ # * `:d315` 45 degrees anti-clockwise
13
+
14
+ class Angle45
15
+ end
16
+ end
@@ -0,0 +1,163 @@
1
+
2
+ module Vips
3
+
4
+ # This class is used internally to convert Ruby values to arguments to
5
+ # libvips operations.
6
+ # @private
7
+ class Argument
8
+ attr_reader :op, :name, :flags, :priority, :isset, :prop
9
+ attr_reader :blurb, :gtype, :type
10
+
11
+ # map gobject-introspection's ruby class names to ours
12
+ @@map_goi_to_vips = {
13
+ "TrueClass" => "Boolean",
14
+ "Vips::ArrayDouble" => "Array<Double>",
15
+ "Vips::ArrayInt" => "Array<Integer>",
16
+ "Vips::ArrayImage" => "Array<Image>",
17
+ "Vips::ArrayString" => "Array<String>",
18
+ }
19
+
20
+ def initialize(op, name)
21
+ @op = op
22
+ @name = name.tr '-', '_'
23
+ @prop = op.gtype.to_class.property name
24
+ @blurb = @prop.blurb
25
+ @gtype = prop.value_type
26
+ @flags = op.get_argument_flags name
27
+ @priority = op.get_argument_priority @name
28
+ @isset = op.argument_isset @name
29
+
30
+ type = GLib::Type[gtype.name].to_class.name
31
+ type = @@map_goi_to_vips[type] if @@map_goi_to_vips.include? type
32
+ @type = type
33
+ end
34
+
35
+ private
36
+
37
+ def self.imageize match_image, value
38
+ return value if match_image == nil
39
+ return value if value.is_a? Vips::Image
40
+
41
+ # 2D array values become tiny 2D images
42
+ if value.is_a? Array and value[0].is_a? Array
43
+ return Vips::Image.new_from_array value
44
+ end
45
+
46
+ # if there's nothing to match to, we also make a 2D image
47
+ if match_image == nil
48
+ return Vips::Image.new_from_array value
49
+ end
50
+
51
+ # we have a 1D array ... use that as a pixel constant and expand
52
+ # to match match_image
53
+ pixel = (Vips::Image.black(1, 1) + value).cast(match_image.format)
54
+ pixel = pixel.copy :interpretation => match_image.interpretation,
55
+ :xres => match_image.xres, :yres => match_image.yres
56
+ pixel.embed(0, 0, match_image.width, match_image.height,
57
+ :extend => :copy)
58
+ end
59
+
60
+ # @private
61
+ class ArrayImageConst < Vips::ArrayImage
62
+ def self.new(value)
63
+ if not value.is_a? Array
64
+ value = [value]
65
+ end
66
+
67
+ match_image = value.find {|x| x.is_a? Vips::Image}
68
+ if match_image == nil
69
+ raise Vips::Error,
70
+ "Argument must contain at least one image."
71
+ end
72
+
73
+ value = value.map {|x| Argument::imageize match_image, x}
74
+
75
+ # we'd like to just
76
+ # super(value)
77
+ # to construct, but the gobject-introspection gem does not
78
+ # support new from object array ... instead, we build in stages
79
+ array = Vips::ArrayImage.empty
80
+ value.each {|x| array = array.append(x)}
81
+
82
+ return array
83
+ end
84
+ end
85
+
86
+ # if this gtype needs an array, try to transform the value into one
87
+ def self.arrayize(gtype, value)
88
+ arrayize_map = {
89
+ GLib::Type["VipsArrayDouble"] => Vips::ArrayDouble,
90
+ GLib::Type["VipsArrayInt"] => Vips::ArrayInt,
91
+ GLib::Type["VipsArrayImage"] => ArrayImageConst
92
+ }
93
+
94
+ if arrayize_map.has_key? gtype
95
+ if not value.is_a? Array
96
+ value = [value]
97
+ end
98
+
99
+ value = arrayize_map[gtype].new(value)
100
+ end
101
+
102
+ value
103
+ end
104
+
105
+ def self.unwrap value
106
+ [Vips::Blob, Vips::ArrayDouble, Vips::ArrayImage,
107
+ Vips::ArrayInt, Vips::RefString].each do |cls|
108
+ if value.is_a? cls
109
+ value, length = value.get
110
+
111
+ # blobs come from gobject-introspection as arrays ...
112
+ # repack as strings for convenience
113
+ if value and cls == Vips::Blob
114
+ value = value.pack("C*")
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ value
122
+ end
123
+
124
+ public
125
+
126
+ def set_value(match_image, value)
127
+ # array-ize
128
+ value = Argument::arrayize gtype, value
129
+
130
+ # blob-ize
131
+ if gtype.type_is_a? GLib::Type["VipsBlob"]
132
+ if not value.is_a? Vips::Blob
133
+ value = Vips::Blob.copy value
134
+ end
135
+ end
136
+
137
+ # image-ize
138
+ if gtype.type_is_a? GLib::Type["VipsImage"]
139
+ if not value.is_a? Vips::Image
140
+ value = Argument::imageize match_image, value
141
+ end
142
+ end
143
+
144
+ # MODIFY input images need to be copied before assigning them
145
+ if (flags & :modify) != 0
146
+ # don't use .copy(): we want to make a new pipeline with no
147
+ # reference back to the old stuff ... this way we can free the
148
+ # previous image earlier
149
+ new_image = Vips::Image.memory
150
+ value.write new_image
151
+ value = new_image
152
+ end
153
+
154
+ op.set_property @name, value
155
+ end
156
+
157
+ def get_value
158
+ Argument::unwrap @op.get_property(@name)
159
+ end
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,20 @@
1
+ module Vips
2
+
3
+ # The format used for each band element. Each corresponds to a native C type
4
+ # for the current machine.
5
+ #
6
+ # * `:notset` invalid setting
7
+ # * `:uchar` unsigned char format
8
+ # * `:char` char format
9
+ # * `:ushort` unsigned short format
10
+ # * `:short` short format
11
+ # * `:uint` unsigned int format
12
+ # * `:int` int format
13
+ # * `:float` float format
14
+ # * `:complex` complex (two floats) format
15
+ # * `:double` double float format
16
+ # * `:dpcomplex` double complex (two double) format
17
+ class BandFormat
18
+ end
19
+
20
+ end
@@ -0,0 +1,302 @@
1
+ module Vips
2
+
3
+ # call a vips operation ... this will crash if there's a GC during the call,
4
+ # I've really no idea why :-(
5
+ #
6
+ # Workaround: don't call this directly, use call_base (see below) instead.
7
+ # This will disable the GC, call this operation, then reenable it.
8
+
9
+ private
10
+ def self.call_base_nogc(name, instance, option_string, supplied_values)
11
+ log "in Vips::call_base"
12
+ log "name = #{name}"
13
+ log "instance = #{instance}"
14
+ log "option_string = #{option_string}"
15
+ log "supplied_values are:"
16
+ supplied_values.each {|x| log " #{x}"}
17
+
18
+ if supplied_values.last.is_a? Hash
19
+ optional_values = supplied_values.last
20
+ supplied_values.delete_at -1
21
+ else
22
+ optional_values = {}
23
+ end
24
+
25
+ begin
26
+ op = Vips::Operation.new name
27
+ rescue
28
+ raise Vips::Error, "no operator '#{name}'"
29
+ end
30
+
31
+ # set string options first
32
+ log "setting string options ..."
33
+ if option_string
34
+ if op.set_from_string(option_string) != 0
35
+ raise Error
36
+ end
37
+ end
38
+
39
+ all_args = op.get_args
40
+
41
+ # the instance, if supplied, must be a vips image ... we use it for
42
+ # match_image, below
43
+ if instance and not instance.is_a? Vips::Image
44
+ raise Vips::Error, "@instance is not a Vips::Image."
45
+ end
46
+
47
+ # if the op needs images but the user supplies constants, we expand
48
+ # them to match the first input image argument ... find the first
49
+ # image
50
+ log "searching for first image argument ..."
51
+ match_image = instance
52
+ if match_image == nil
53
+ match_image = supplied_values.find {|x| x.is_a? Vips::Image}
54
+ end
55
+ if match_image == nil
56
+ match = optional_values.find do |name, value|
57
+ value.is_a? Vips::Image
58
+ end
59
+ # if we found a match, it'll be [name, value]
60
+ if match
61
+ match_image = match[1]
62
+ end
63
+ end
64
+
65
+ # find unassigned required input args
66
+ log "finding unassigned required input arguments ..."
67
+ required_input = all_args.select do |arg|
68
+ not arg.isset and
69
+ (arg.flags & :input) != 0 and
70
+ (arg.flags & :required) != 0
71
+ end
72
+
73
+ # do we have a non-nil instance? set the first image arg with this
74
+ if instance != nil
75
+ log "setting first image arg with instance ..."
76
+ x = required_input.find do |arg|
77
+ gtype = GLib::Type["VipsImage"]
78
+ vtype = arg.prop.value_type
79
+
80
+ vtype.type_is_a? gtype
81
+ end
82
+ if x == nil
83
+ raise Vips::Error,
84
+ "No #{instance.class} argument to #{name}."
85
+ end
86
+ x.set_value match_image, instance
87
+ required_input.delete x
88
+ end
89
+
90
+ if required_input.length != supplied_values.length
91
+ raise Vips::Error,
92
+ "Wrong number of arguments. '#{name}' requires " +
93
+ "#{required_input.length} arguments, you supplied " +
94
+ "#{supplied_values.length}."
95
+ end
96
+
97
+ log "setting required input arguments ..."
98
+ required_input.zip(supplied_values).each do |arg, value|
99
+ arg.set_value match_image, value
100
+ end
101
+
102
+ # find optional unassigned input args
103
+ log "finding optional unassigned input arguments ..."
104
+ optional_input = all_args.select do |arg|
105
+ not arg.isset and
106
+ (arg.flags & :input) != 0 and
107
+ (arg.flags & :required) == 0
108
+ end
109
+
110
+ # make a hash from name to arg
111
+ optional_input = Hash[
112
+ optional_input.map(&:name).zip(optional_input)]
113
+
114
+ # find optional unassigned output args
115
+ log "finding optional unassigned output arguments ..."
116
+ optional_output = all_args.select do |arg|
117
+ not arg.isset and
118
+ (arg.flags & :output) != 0 and
119
+ (arg.flags & :required) == 0
120
+ end
121
+ optional_output = Hash[
122
+ optional_output.map(&:name).zip(optional_output)]
123
+
124
+ # set all optional args
125
+ log "setting optional values ..."
126
+ optional_values.each do |name, value|
127
+ # we are passed symbols as keys
128
+ name = name.to_s
129
+ if optional_input.has_key? name
130
+ log "setting #{name} to #{value}"
131
+ optional_input[name].set_value match_image, value
132
+ elsif optional_output.has_key? name and value != true
133
+ raise Vips::Error,
134
+ "Optional output argument #{name} must be true."
135
+ elsif not optional_output.has_key? name
136
+ raise Vips::Error, "No such option '#{name}',"
137
+ end
138
+ end
139
+
140
+ log "building ..."
141
+
142
+ op2 = Vips::cache_operation_lookup op
143
+ if op2
144
+ log "cache hit"
145
+ op = op2
146
+
147
+ all_args = op.get_args
148
+
149
+ # find optional output args
150
+ optional_output = all_args.select do |arg|
151
+ (arg.flags & :output) != 0 and
152
+ (arg.flags & :required) == 0
153
+ end
154
+ optional_output = Hash[
155
+ optional_output.map(&:name).zip(optional_output)]
156
+ else
157
+ log "cache miss ... building"
158
+ if not op.build
159
+ raise Vips::Error
160
+ end
161
+ showall
162
+
163
+ log "adding to cache ... "
164
+ Vips::cache_operation_add op
165
+ end
166
+
167
+ log "fetching outputs ..."
168
+
169
+ # gather output args
170
+ out = []
171
+
172
+ all_args.each do |arg|
173
+ # required output
174
+ if (arg.flags & :output) != 0 and
175
+ (arg.flags & :required) != 0
176
+ log "fetching required output #{arg.name}"
177
+ out << arg.get_value
178
+ end
179
+
180
+ # modified input arg ... this will get the result of the
181
+ # copy() we did in Argument.set_value above
182
+ if (arg.flags & :input) != 0 and
183
+ (arg.flags & :modify) != 0
184
+ log "fetching modified input arg ..."
185
+ out << arg.get_value
186
+ end
187
+ end
188
+
189
+ opts = {}
190
+ optional_values.each do |name, value|
191
+ # we are passed symbols as keys
192
+ name = name.to_s
193
+ if optional_output.has_key? name
194
+ log "fetching optional output arg ..."
195
+ opts[name] = optional_output[name].get_value
196
+ end
197
+ end
198
+ out << opts if opts != {}
199
+
200
+ if out.length == 1
201
+ out = out[0]
202
+ elsif out.length == 0
203
+ out = nil
204
+ end
205
+
206
+ log "unreffing outputs ..."
207
+ op.unref_outputs
208
+ op = nil
209
+ # showall
210
+
211
+ log "success! #{name}.out = #{out}"
212
+
213
+ return out
214
+ end
215
+
216
+ # run call_base_nogc, with the GC disabled
217
+ private
218
+ def self.call_base(name, instance, option_string, supplied_values)
219
+ gc_was_enabled = GC.disable
220
+ begin
221
+ result = call_base_nogc name, instance,
222
+ option_string, supplied_values
223
+ ensure
224
+ GC.enable if gc_was_enabled
225
+ end
226
+
227
+ return result
228
+ end
229
+
230
+ public
231
+
232
+ # This is the public entry point for the vips8 binding. {call} will run
233
+ # any vips operation, for example:
234
+ #
235
+ # ```ruby
236
+ # out = Vips::call "black", 100, 100, :bands => 12
237
+ # ```
238
+ #
239
+ # will call the C function
240
+ #
241
+ # ```C
242
+ # vips_black( &out, 100, 100, "bands", 12, NULL );
243
+ # ```
244
+ #
245
+ # There are {Image#method_missing} hooks which will run {call} for you
246
+ # on {Image} for undefined instance or class methods. So you can also
247
+ # write:
248
+ #
249
+ # ```ruby
250
+ # out = Vips::Image.black 100, 100, :bands => 12
251
+ # ```
252
+ #
253
+ # Or perhaps:
254
+ #
255
+ # ```ruby
256
+ # x = Vips::Image.black 100, 100
257
+ # y = x.invert
258
+ # ```
259
+ #
260
+ # to run the `vips_invert()` operator.
261
+ #
262
+ # There are also a set of operator overloads and some convenience functions,
263
+ # see {Image}.
264
+ #
265
+ # If the operator needs a vector constant, {call} will turn a scalar into a
266
+ # vector for you. So for `x.linear(a, b)`, which calculates
267
+ # `x * a + b` where `a` and `b` are vector constants, you can write:
268
+ #
269
+ # ```ruby
270
+ # x = Vips::Image.black 100, 100, :bands => 3
271
+ # y = x.linear(1, 2)
272
+ # y = x.linear([1], 4)
273
+ # y = x.linear([1, 2, 3], 4)
274
+ # ```
275
+ #
276
+ # or any other combination. The operator overloads use this facility to
277
+ # support all the variations on:
278
+ #
279
+ # ```ruby
280
+ # x = Vips::Image.black 100, 100, :bands => 3
281
+ # y = x * 2
282
+ # y = x + [1,2,3]
283
+ # y = x % [1]
284
+ # ```
285
+ #
286
+ # Similarly, whereever an image is required, you can use a constant. The
287
+ # constant will be expanded to an image matching the first input image
288
+ # argument. For example, you can write:
289
+ #
290
+ # ```
291
+ # x = Vips::Image.black 100, 100, :bands => 3
292
+ # y = x.bandjoin(255)
293
+ # ```
294
+ #
295
+ # to add an extra band to the image where each pixel in the new band has
296
+ # the constant value 255.
297
+
298
+ def self.call(name, *args)
299
+ Vips::call_base name, nil, "", args
300
+ end
301
+
302
+ end