lib/vips/image.rb in vips-8.8.4 vs lib/vips/image.rb in vips-8.9.1
- old
+ new
@@ -12,49 +12,53 @@
attach_function :vips_image_new_matrix_from_array,
[:int, :int, :pointer, :int], :pointer
attach_function :vips_image_copy_memory, [:pointer], :pointer
+ attach_function :vips_image_set_progress, [:pointer, :bool], :void
+ attach_function :vips_image_set_kill, [:pointer, :bool], :void
+
attach_function :vips_filename_get_filename, [:string], :pointer
attach_function :vips_filename_get_options, [:string], :pointer
attach_function :vips_foreign_find_load, [:string], :string
attach_function :vips_foreign_find_save, [:string], :string
attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
attach_function :vips_foreign_find_save_buffer, [:string], :string
+ if Vips::at_least_libvips?(8, 9)
+ attach_function :vips_foreign_find_load_source, [:pointer], :string
+ attach_function :vips_foreign_find_save_target, [:string], :string
+ end
+
attach_function :vips_image_write_to_memory,
[:pointer, SizeStruct.ptr], :pointer
attach_function :vips_image_get_typeof, [:pointer, :string], :GType
attach_function :vips_image_get,
[:pointer, :string, GObject::GValue.ptr], :int
- # vips_image_get_fields was added in libvips 8.5
- begin
+ if Vips::at_least_libvips?(8, 5)
attach_function :vips_image_get_fields, [:pointer], :pointer
- rescue FFI::NotFoundError
- nil
+ attach_function :vips_image_hasalpha, [:pointer], :int
end
- # vips_addalpha was added in libvips 8.6
if Vips::at_least_libvips?(8, 6)
attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
end
- if Vips::at_least_libvips?(8, 5)
- attach_function :vips_image_hasalpha, [:pointer], :int
- end
attach_function :vips_image_set,
[:pointer, :string, GObject::GValue.ptr], :void
attach_function :vips_image_remove, [:pointer, :string], :void
attach_function :vips_band_format_iscomplex, [:int], :int
attach_function :vips_band_format_isfloat, [:int], :int
attach_function :nickname_find, :vips_nickname_find, [:GType], :string
+ attach_function :vips_image_invalidate_all, [:pointer], :void
+
# turn a raw pointer that must be freed into a self-freeing Ruby string
def self.p2str(pointer)
pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
pointer.read_string
end
@@ -65,10 +69,14 @@
# for an introduction to using this class.
class Image < Vips::Object
alias_method :parent_get_typeof, :get_typeof
+ def close
+ Vips.vips_image_invalidate_all(self)
+ end
+
private
# the layout of the VipsImage struct
module ImageLayout
def self.included base
@@ -113,11 +121,11 @@
def self.run_cmplx image, &block
original_format = image.format
unless Image::complex? image.format
if image.bands % 2 != 0
- raise Error, "not an even number of bands"
+ raise Vips::Error, "not an even number of bands"
end
unless Image::float? image.format
image = image.cast :float
end
@@ -143,38 +151,10 @@
else
Vips::Operation.call name.to_s + "_const", [self, enum, other]
end
end
- # Write can fail due to no file descriptors and memory can fill if
- # large objects are not collected fairly soon. We can't try a
- # write and GC and retry on fail, since the write may take a
- # long time and may not be repeatable.
- #
- # GCing before every write would have a horrible effect on
- # performance, so as a compromise we GC every @@gc_interval writes.
- #
- # ruby2.1 introduced a generational GC which is fast enough to be
- # able to GC on every write.
-
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
-
- @@gc_interval = 100
- @@gc_countdown = @@gc_interval
-
- def write_gc
- if @@generational_gc
- GC.start full_mark: false
- else
- @@gc_countdown -= 1
- if @@gc_countdown < 0
- @@gc_countdown = @@gc_interval
- GC.start
- end
- end
- end
-
public
def inspect
"#<Image #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
end
@@ -217,17 +197,17 @@
# Return a new {Image} for a file on disc. This method can load
# images in any format supported by vips. The filename can include
# load options, for example:
#
# ```
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
# ```
#
# You can also supply options as a hash, for example:
#
# ```
- # image = Vips::new_from_file "fred.jpg", shrink: 2
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
# ```
#
# The full set of options available depend upon the load operation that
# will be executed. Try something like:
#
@@ -294,15 +274,54 @@
# @param option_string [String] load options as a string
# @macro vips.loadopts
# @return [Image] the loaded image
def self.new_from_buffer data, option_string, **opts
loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
- raise Vips::Error if loader == nil
+ raise Vips::Error if loader.nil?
Vips::Operation.call loader, [data], opts, option_string
end
+ # Create a new {Image} from a source. Load options may be passed as
+ # strings or appended as a hash. For example:
+ #
+ # ```
+ # source = Vips::Source.new_from_file("k2.jpg")
+ # image = Vips::Image.new_from_source source, "shrink=2"
+ # ```
+ #
+ # or alternatively:
+ #
+ # ```
+ # image = Vips::Image.new_from_source source, "", shrink: 2
+ # ```
+ #
+ # The options available depend on the file format. Try something like:
+ #
+ # ```
+ # $ vips jpegload_source
+ # ```
+ #
+ # at the command-line to see the available options. Not all loaders
+ # support load from source, but at least JPEG, PNG and
+ # TIFF images will work.
+ #
+ # Loading is fast: only enough data is read to be able to fill
+ # out the header. Pixels will only be read and decompressed when they are
+ # needed.
+ #
+ # @param source [Vips::Source] the source to load from
+ # @param option_string [String] load options as a string
+ # @macro vips.loadopts
+ # @return [Image] the loaded image
+ def self.new_from_source source, option_string, **opts
+ loader = Vips::vips_foreign_find_load_source source
+ raise Vips::Error if loader.nil?
+
+ Vips::Operation.call loader, [source], opts, option_string
+ end
+
def self.matrix_from_array width, height, array
ptr = FFI::MemoryPointer.new :double, array.length
ptr.write_array_of_double array
image = Vips::vips_image_new_matrix_from_array width, height,
ptr, array.length
@@ -315,17 +334,17 @@
# convolutions.
#
# For example:
#
# ```
- # image = Vips::new_from_array [1, 2, 3]
+ # image = Vips::Image.new_from_array [1, 2, 3]
# ```
#
# or
#
# ```
- # image = Vips::new_from_array [
+ # image = Vips::Image.new_from_array [
# [-1, -1, -1],
# [-1, 16, -1],
# [-1, -1, -1]], 8
# ```
#
@@ -424,12 +443,10 @@
if saver == nil
raise Vips::Error, "No known saver for '#{filename}'."
end
Vips::Operation.call saver, [self, filename], opts, option_string
-
- write_gc
end
# Write this image to a memory buffer. Save options may be encoded in
# the format_string or given as a hash. For example:
#
@@ -458,21 +475,56 @@
def write_to_buffer format_string, **opts
filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
saver = Vips::vips_foreign_find_save_buffer filename
if saver == nil
- raise Vips::Error, "No known saver for '#{filename}'."
+ raise Vips::Error, "No known buffer saver for '#{filename}'."
end
buffer = Vips::Operation.call saver, [self], opts, option_string
raise Vips::Error if buffer == nil
- write_gc
-
return buffer
end
+ # Write this image to a target. Save options may be encoded in
+ # the format_string or given as a hash. For example:
+ #
+ # ```ruby
+ # target = Vips::Target.new_to_file "k2.jpg"
+ # image.write_to_target target, ".jpg[Q=90]"
+ # ```
+ #
+ # or equivalently:
+ #
+ # ```ruby
+ # image.write_to_target target, ".jpg", Q: 90
+ # ```
+ #
+ # The full set of save options depend on the selected saver. Try
+ # something like:
+ #
+ # ```
+ # $ vips jpegsave_target
+ # ```
+ #
+ # to see all the available options for JPEG save.
+ #
+ # @param target [Vips::Target] the target to write to
+ # @param format_string [String] save format plus string options
+ # @macro vips.saveopts
+ def write_to_target target, format_string, **opts
+ filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
+ option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
+ saver = Vips::vips_foreign_find_save_target filename
+ if saver == nil
+ raise Vips::Error, "No known target saver for '#{filename}'."
+ end
+
+ Vips::Operation.call saver, [self, target], opts, option_string
+ end
+
# Write this image to a large memory buffer.
#
# @return [String] the pixels as a huge binary string
def write_to_memory
len = Vips::SizeStruct.new
@@ -483,10 +535,32 @@
ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
ptr.get_bytes 0, len[:value]
end
+ # Turn progress signalling on and off.
+ #
+ # If this is on, the most-downstream image from this image will issue
+ # progress signals.
+ #
+ # @see Object#signal_connect
+ # @param state [Boolean] progress signalling state
+ def set_progress state
+ Vips::vips_image_set_progress self, state
+ end
+
+ # Kill computation of this time.
+ #
+ # Set true to stop computation of this image. You can call this from a
+ # progress handler, for example.
+ #
+ # @see Object#signal_connect
+ # @param kill [Boolean] stop computation
+ def set_kill kill
+ Vips::vips_image_set_kill self, kill
+ end
+
# Get the `GType` of a metadata field. The result is 0 if no such field
# exists.
#
# @see get
# @param name [String] Metadata field to fetch
@@ -521,14 +595,15 @@
unless Vips::at_least_libvips?(8, 5)
return super if parent_get_typeof(name) != 0
end
gvalue = GObject::GValue.alloc
- result = Vips::vips_image_get self, name, gvalue
- raise Vips::Error if result != 0
+ raise Vips::Error if Vips::vips_image_get(self, name, gvalue) != 0
+ result = gvalue.get
+ gvalue.unset
- gvalue.get
+ result
end
# Get the names of all fields on an image. Use this to loop over all
# image metadata.
#
@@ -569,10 +644,11 @@
def set_type gtype, name, value
gvalue = GObject::GValue.alloc
gvalue.init gtype
gvalue.set value
Vips::vips_image_set self, name, gvalue
+ gvalue.unset
end
# Set the value of a metadata item on an image. The metadata item must
# already exist. Ruby types are automatically transformed into the
# matching `GValue`, if possible.
@@ -1057,11 +1133,11 @@
# @param mode [BlendMode, Array<BlendMode>] blend modes to use
# @param opts [Hash] Set of options
# @option opts [Vips::Interpretation] :compositing_space Composite images in this colour space
# @option opts [Boolean] :premultiplied Images have premultiplied alpha
# @return [Image] blended image
- def composite overlay, mode, **opts
+ def composite overlay, mode, **options
unless overlay.is_a? Array
overlay = [overlay]
end
unless mode.is_a? Array
mode = [mode]
@@ -1069,11 +1145,11 @@
mode = mode.map do |x|
GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
end
- Vips::Image.composite([self] + overlay, mode, opts)
+ Vips::Image.composite([self] + overlay, mode, **options)
end
# Return the coordinates of the image maximum.
#
# @return [Real, Real, Real] maximum value, x coordinate of maximum, y
@@ -1318,182 +1394,174 @@
# Scale an image to uchar. This is the vips `scale` operation, but
# renamed to avoid a clash with the `.scale` property.
#
# @param opts [Hash] Set of options
# @return [Vips::Image] Output image
- def scaleimage **opts
- Vips::Image.scale self, opts
+ def scaleimage **options
+ Vips::Image.scale self, **options
end
end
end
module Vips
- # This method generates yard comments for all the dynamically bound
+ # This module generates yard comments for all the dynamically bound
# vips operations.
#
# Regenerate with something like:
#
# ```
# $ ruby > methods.rb
- # require 'vips'; Vips::generate_yard
+ # require 'vips'; Vips::Yard.generate
# ^D
# ```
- def self.generate_yard
- # these have hand-written methods, see above
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
-
+ module Yard
# map gobject's type names to Ruby
- map_go_to_ruby = {
+ MAP_GO_TO_RUBY = {
"gboolean" => "Boolean",
"gint" => "Integer",
"gdouble" => "Float",
"gfloat" => "Float",
"gchararray" => "String",
"VipsImage" => "Vips::Image",
"VipsInterpolate" => "Vips::Interpolate",
+ "VipsConnection" => "Vips::Connection",
+ "VipsSource" => "Vips::Source",
+ "VipsTarget" => "Vips::Target",
+ "VipsSourceCustom" => "Vips::SourceCustom",
+ "VipsTargetCustom" => "Vips::TargetCustom",
"VipsArrayDouble" => "Array<Double>",
"VipsArrayInt" => "Array<Integer>",
"VipsArrayImage" => "Array<Image>",
"VipsArrayString" => "Array<String>",
}
- generate_operation = lambda do |gtype, nickname, op|
- op_flags = op.get_flags
- return if (op_flags & OPERATION_DEPRECATED) != 0
- return if no_generate.include? nickname
+ # these have hand-written methods, see above
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
- description = Vips::vips_object_get_description op
+ # these are aliased (appear under several names)
+ ALIAS = ["crop"]
- # find and classify all the arguments the operator can take
- required_input = []
- optional_input = []
- required_output = []
- optional_output = []
- member_x = nil
- op.argument_map do |pspec, argument_class, _argument_instance|
- arg_flags = argument_class[:flags]
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
+ # turn a gtype into a ruby type name
+ def self.gtype_to_ruby gtype
+ fundamental = GObject::g_type_fundamental gtype
+ type_name = GObject::g_type_name gtype
- name = pspec[:name].tr("-", "_")
- # 'in' as a param name confuses yard
- name = "im" if name == "in"
- gtype = pspec[:value_type]
- fundamental = GObject::g_type_fundamental gtype
- type_name = GObject::g_type_name gtype
- if map_go_to_ruby.include? type_name
- type_name = map_go_to_ruby[type_name]
- end
- if fundamental == GObject::GFLAGS_TYPE ||
- fundamental == GObject::GENUM_TYPE
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
- end
- blurb = GObject::g_param_spec_get_blurb pspec
- value = {
- name: name,
- flags: arg_flags,
- gtype: gtype,
- type_name: type_name,
- blurb: blurb
- }
+ if MAP_GO_TO_RUBY.include? type_name
+ type_name = MAP_GO_TO_RUBY[type_name]
+ end
- if (arg_flags & ARGUMENT_INPUT) != 0
- if (arg_flags & ARGUMENT_REQUIRED) != 0
- # note the first required input image, if any ... we
- # will be a method of this instance
- if !member_x && gtype == Vips::IMAGE_TYPE
- member_x = value
- else
- required_input << value
- end
- else
- optional_input << value
- end
- end
-
- # MODIFY INPUT args count as OUTPUT as well
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
- (arg_flags & ARGUMENT_MODIFY) != 0)
- if (arg_flags & ARGUMENT_REQUIRED) != 0
- required_output << value
- else
- optional_output << value
- end
- end
+ if fundamental == GObject::GFLAGS_TYPE ||
+ fundamental == GObject::GENUM_TYPE
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
end
+ type_name
+ end
+
+ def self.generate_operation introspect
+ return if (introspect.flags & OPERATION_DEPRECATED) != 0
+ return if NO_GENERATE.include? introspect.name
+
+ method_args = introspect.method_args
+ required_output = introspect.required_output
+ optional_input = introspect.optional_input
+ optional_output = introspect.optional_output
+
print "# @!method "
- print "self." unless member_x
- print "#{nickname}("
- print required_input.map { |x| x[:name] }.join(", ")
- print ", " if required_input.length > 0
+ print "self." unless introspect.member_x
+ print "#{introspect.name}("
+ print method_args.map{ |x| x[:yard_name] }.join(", ")
+ print ", " if method_args.length > 0
puts "**opts)"
- puts "# #{description.capitalize}."
+ puts "# #{introspect.description.capitalize}."
- required_input.each do |arg|
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] #{arg[:blurb]}"
+ method_args.each do |details|
+ yard_name = details[:yard_name]
+ gtype = details[:gtype]
+ blurb = details[:blurb]
+
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
end
puts "# @param opts [Hash] Set of options"
- optional_input.each do |arg|
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
- "#{arg[:blurb]}"
+ optional_input.each do |arg_name, details|
+ yard_name = details[:yard_name]
+ gtype = details[:gtype]
+ blurb = details[:blurb]
+
+ puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} " +
+ "#{blurb}"
end
- optional_output.each do |arg|
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
- puts " Output #{arg[:blurb]}"
+ optional_output.each do |arg_name, details|
+ yard_name = details[:yard_name]
+ gtype = details[:gtype]
+ blurb = details[:blurb]
+
+ print "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name}"
+ puts " Output #{blurb}"
end
print "# @return ["
if required_output.length == 0
print "nil"
elsif required_output.length == 1
- print required_output.first[:type_name]
+ print gtype_to_ruby(required_output.first[:gtype])
else
print "Array<"
- print required_output.map { |x| x[:type_name] }.join(", ")
+ print required_output.map{ |x| gtype_to_ruby(x[:gtype]) }.join(", ")
print ">"
end
if optional_output.length > 0
print ", Hash<Symbol => Object>"
end
print "] "
- print required_output.map { |x| x[:blurb] }.join(", ")
+ print required_output.map{ |x| x[:blurb] }.join(", ")
if optional_output.length > 0
print ", " if required_output.length > 0
print "Hash of optional output items"
end
puts ""
puts ""
end
- generate_class = lambda do |gtype, _|
- nickname = Vips::nickname_find gtype
+ def self.generate
+ alias_gtypes = {}
+ ALIAS.each do |name|
+ gtype = Vips::type_find "VipsOperation", name
+ alias_gtypes[gtype] = name
+ end
- if nickname
- begin
- # can fail for abstract types
- op = Vips::Operation.new nickname
- rescue Vips::Error
- nil
+ generate_class = lambda do |gtype, _|
+ if alias_gtypes.key? gtype
+ name = alias_gtypes[gtype]
+ else
+ name = Vips::nickname_find gtype
end
- generate_operation.(gtype, nickname, op) if op
- end
+ if name
+ begin
+ # can fail for abstract types
+ introspect = Vips::Introspect.get_yard name
+ rescue Vips::Error
+ nil
+ end
- Vips::vips_type_map gtype, generate_class, nil
- end
+ generate_operation(introspect) if introspect
+ end
- puts "module Vips"
- puts " class Image"
- puts ""
+ Vips::vips_type_map gtype, generate_class, nil
+ end
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
+ puts "module Vips"
+ puts " class Image"
+ puts ""
- puts " end"
- puts "end"
+ generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
+
+ puts " end"
+ puts "end"
+ end
end
end