lib/vips/image.rb in vips-8.11.3 vs lib/vips/image.rb in vips-8.12.1
- old
+ new
@@ -2,17 +2,17 @@
# via ruby-ffi.
#
# Author:: John Cupitt (mailto:jcupitt@gmail.com)
# License:: MIT
-require 'ffi'
+require "ffi"
module Vips
private
attach_function :vips_image_new_matrix_from_array,
- [:int, :int, :pointer, :int], :pointer
+ [: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
@@ -23,42 +23,51 @@
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)
+ 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
+ [:pointer, SizeStruct.ptr], :pointer
attach_function :vips_image_get_typeof, [:pointer, :string], :GType
attach_function :vips_image_get,
- [:pointer, :string, GObject::GValue.ptr], :int
+ [:pointer, :string, GObject::GValue.ptr], :int
- if Vips::at_least_libvips?(8, 5)
+ attach_function :vips_image_get_width, [:pointer], :int
+ attach_function :vips_image_get_height, [:pointer], :int
+ attach_function :vips_image_get_bands, [:pointer], :int
+
+ if Vips.at_least_libvips?(8, 5)
attach_function :vips_image_get_fields, [:pointer], :pointer
attach_function :vips_image_hasalpha, [:pointer], :int
end
- if Vips::at_least_libvips?(8, 6)
+ if Vips.at_least_libvips?(8, 6)
attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
end
+ # move these three lines to mutableimage when we finally remove set and
+ # remove in this class
attach_function :vips_image_set,
- [:pointer, :string, GObject::GValue.ptr], :void
+ [: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
+ attach_function :vips_image_new_from_memory, [:pointer, :size_t, :int, :int, :int, :int], :pointer
+ attach_function :vips_image_new_from_memory_copy, [:pointer, :size_t, :int, :int, :int, :int], :pointer
+
# 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
@@ -73,10 +82,15 @@
def close
Vips.vips_image_invalidate_all(self)
end
+ # FFI sets a pointer's size to this magic value if the size of the memory
+ # chunk the pointer points to is unknown to FFI.
+ UNKNOWN_POINTER_SIZE = FFI::Pointer.new(1).size
+ private_constant :UNKNOWN_POINTER_SIZE
+
private
# the layout of the VipsImage struct
module ImageLayout
def self.included base
@@ -100,45 +114,45 @@
end
# handy for overloads ... want to be able to apply a function to an
# array or to a scalar
def self.smap x, &block
- x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.(x)
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.call(x)
end
def self.complex? format
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
- Vips::vips_band_format_iscomplex(format_number) != 0
+ Vips.vips_band_format_iscomplex(format_number) != 0
end
def self.float? format
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
- Vips::vips_band_format_isfloat(format_number) != 0
+ Vips.vips_band_format_isfloat(format_number) != 0
end
# run a complex operation on a complex image, or an image with an even
# number of bands ... handy for things like running .polar on .index
# images
def self.run_cmplx image, &block
original_format = image.format
- unless Image::complex? image.format
+ unless Image.complex? image.format
if image.bands % 2 != 0
raise Vips::Error, "not an even number of bands"
end
- unless Image::float? image.format
+ unless Image.float? image.format
image = image.cast :float
end
new_format = image.format == :double ? :dpcomplex : :complex
image = image.copy format: new_format, bands: image.bands / 2
end
- image = block.(image)
+ image = block.call(image)
- unless Image::complex? original_format
+ unless Image.complex? original_format
new_format = image.format == :dpcomplex ? :double : :float
image = image.copy format: new_format, bands: image.bands * 2
end
image
@@ -165,19 +179,23 @@
#
# https://makandracards.com/makandra/
# 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
return false if name == :to_hash
+ super
+ end
+
+ def respond_to_missing? name, include_all = false
# respond to all vips operations by nickname
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
super
end
- def self.respond_to? name, include_all = false
+ def self.respond_to_missing? name, include_all = false
# respond to all vips operations by nickname
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
super
end
# Invoke a vips operation with {Vips::Operation.call}, using self as
@@ -231,22 +249,22 @@
# @macro vips.loadopts
# @return [Image] the loaded image
def self.new_from_file name, **opts
# very common, and Vips::vips_filename_get_filename will segv if we
# pass this
- raise Vips::Error, "filename is nil" if name == nil
+ raise Vips::Error, "filename is nil" if name.nil?
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
- loader = Vips::vips_foreign_find_load filename
- raise Vips::Error if loader == nil
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
+ loader = Vips.vips_foreign_find_load filename
+ raise Vips::Error if loader.nil?
Operation.call loader, [filename], opts, option_string
end
- # Create a new {Image} for an image encoded, in a format such as
- # JPEG, in a binary string. Load options may be passed as
+ # Create a new {Image} for an image encoded in a format such as
+ # JPEG in a binary string. Load options may be passed as
# strings or appended as a hash. For example:
#
# ```
# image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
# ```
@@ -273,16 +291,117 @@
# @param data [String] the data to load from
# @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
+ loader = Vips.vips_foreign_find_load_buffer data, data.bytesize
raise Vips::Error if loader.nil?
Vips::Operation.call loader, [data], opts, option_string
end
+ # Create a new {Image} from a C-style array held in memory. For example:
+ #
+ # ```
+ # image = Vips::Image.black(16, 16) + 128
+ # data = image.write_to_memory
+ #
+ # x = Vips::Image.new_from_memory data,
+ # image.width, image.height, image.bands, image.format
+ # ```
+ #
+ # Creating a new image from a memory pointer:
+ #
+ # ```
+ # ptr = FFI::MemoryPointer.new(:uchar, 10*10)
+ # # => #<FFI::MemoryPointer address=0x00007fc236db31d0 size=100>
+ # x = Vips::Image.new_from_memory(ptr, 10, 10, 1, :uchar)
+ # ```
+ #
+ # Creating a new image from an address only pointer:
+ #
+ # ```
+ # ptr = call_to_external_c_library(w: 10, h: 10)
+ # # => #<FFI::Pointer address=0x00007f9780813a00>
+ # ptr_slice = ptr.slice(0, 10*10)
+ # # => #<FFI::Pointer address=0x00007f9780813a00 size=100>
+ # x = Vips::Image.new_from_memory(ptr_slice, 10, 10, 1, :uchar)
+ # ```
+ #
+ # {new_from_memory} keeps a reference to the array of pixels you pass in
+ # to try to prevent that memory from being freed by the Ruby GC while it
+ # is being used.
+ #
+ # See {new_from_memory_copy} for a version of this method which does not
+ # keep a reference.
+ #
+ # @param data [String, FFI::Pointer] the data to load from
+ # @param width [Integer] width in pixels
+ # @param height [Integer] height in pixels
+ # @param bands [Integer] number of bands
+ # @param format [Symbol] band format
+ # @return [Image] the loaded image
+ def self.new_from_memory data, width, height, bands, format
+ # prevent data from being freed with JRuby FFI
+ if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
+ data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
+ end
+
+ if data.is_a?(FFI::Pointer)
+ # A pointer needs to know about the size of the memory it points to.
+ # If you have an address-only pointer, use the .slice method to wrap
+ # the pointer in a size aware pointer.
+ if data.size == UNKNOWN_POINTER_SIZE
+ raise Vips::Error, "size of memory is unknown"
+ end
+ size = data.size
+ else
+ size = data.bytesize
+ end
+
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
+ vi = Vips.vips_image_new_from_memory data, size,
+ width, height, bands, format_number
+ raise Vips::Error if vi.null?
+ image = new(vi)
+
+ # keep a secret ref to the underlying object .. this reference will be
+ # inherited by things that in turn depend on us, so the memory we are
+ # using will not be freed
+ image.references << data
+
+ image
+ end
+
+ # Create a new {Image} from memory and copies the memory area. See
+ # {new_from_memory} for a version of this method which does not copy the
+ # memory area.
+ #
+ # @param data [String, FFI::Pointer] the data to load from
+ # @param width [Integer] width in pixels
+ # @param height [Integer] height in pixels
+ # @param bands [Integer] number of bands
+ # @param format [Symbol] band format
+ # @return [Image] the loaded image
+ def self.new_from_memory_copy data, width, height, bands, format
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
+
+ if data.is_a?(FFI::Pointer)
+ if data.size == UNKNOWN_POINTER_SIZE
+ raise Vips::Error, "size of memory is unknown"
+ end
+ size = data.size
+ else
+ size = data.bytesize
+ end
+
+ vi = Vips.vips_image_new_from_memory_copy data, size,
+ width, height, bands, format_number
+ raise Vips::Error if vi.null?
+ new(vi)
+ 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")
@@ -304,29 +423,29 @@
# 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
+ # 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
+ 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
+ image = Vips.vips_image_new_matrix_from_array width, height,
+ ptr, array.length
Vips::Image.new image
end
# Create a new Image from a 1D or 2D array. A 1D array becomes an
# image with height 1. Use `scale` and `offset` to set the scale and
@@ -375,22 +494,26 @@
else
height = 1
width = array.length
end
+ unless array.length == width * height
+ raise Vips::Error, "Bad array dimensions."
+ end
+
unless array.all? { |x| x.is_a? Numeric }
raise Vips::Error, "Not all array elements are Numeric."
end
image = Vips::Image.matrix_from_array width, height, array
- raise Vips::Error if image == nil
+ raise Vips::Error if image.nil?
- # be careful to set them as double
- image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
- image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
-
- return image
+ image.mutate do |mutable|
+ # be careful to set them as double
+ mutable.set_type! GObject::GDOUBLE_TYPE, "scale", scale.to_f
+ mutable.set_type! GObject::GDOUBLE_TYPE, "offset", offset.to_f
+ end
end
# A new image is created with the same width, height, format,
# interpretation, resolution and offset as self, but with every pixel
# set to the specified value.
@@ -435,18 +558,20 @@
# @option opts [Array<Float>] :background (0) Background colour to
# flatten alpha against, if necessary
#
# @param name [String] filename to write to
def write_to_file name, **opts
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
- saver = Vips::vips_foreign_find_save filename
- if saver == nil
- raise Vips::Error, "No known saver for '#{filename}'."
- end
+ raise Vips::Error, "filename is nil" if name.nil?
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
+ saver = Vips.vips_foreign_find_save filename
+ raise Vips::Error if saver.nil?
+
Vips::Operation.call saver, [self, filename], opts, option_string
+
+ GC.start(full_mark: false)
end
# Write this image to a memory buffer. Save options may be encoded in
# the format_string or given as a hash. For example:
#
@@ -471,20 +596,37 @@
#
# @param format_string [String] save format plus options
# @macro vips.saveopts
# @return [String] the image saved in the specified format
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 buffer saver for '#{filename}'."
+ raise Vips::Error, "filename is nil" if format_string.nil?
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
+
+ # try to save with the new target API first, only fall back to the old
+ # buffer API if there's no target save for this filetype
+ saver = nil
+ if Vips.at_least_libvips?(8, 9)
+ Vips.vips_error_freeze
+ saver = Vips.vips_foreign_find_save_target filename
+ Vips.vips_error_thaw
end
- buffer = Vips::Operation.call saver, [self], opts, option_string
- raise Vips::Error if buffer == nil
+ if !saver.nil?
+ target = Vips::Target.new_to_memory
+ Vips::Operation.call saver, [self, target], opts, option_string
+ buffer = target.get("blob")
+ else
+ saver = Vips.vips_foreign_find_save_buffer filename
+ raise Vips::Error if saver.nil?
+ buffer = Vips::Operation.call saver, [self], opts, option_string
+ raise Vips::Error if buffer.nil?
+ end
+
+ GC.start(full_mark: false)
+
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:
@@ -511,54 +653,59 @@
#
# @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
+ raise Vips::Error, "filename is nil" if format_string.nil?
+ 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
+ raise Vips::Error if saver.nil?
Vips::Operation.call saver, [self, target], opts, option_string
+
+ GC.start(full_mark: false)
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
- ptr = Vips::vips_image_write_to_memory self, len
- raise Vips::Error if ptr == nil
+ ptr = Vips.vips_image_write_to_memory self, len
+ raise Vips::Error if ptr.nil?
# wrap up as an autopointer
ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
- ptr.get_bytes 0, len[:value]
+ data = ptr.get_bytes 0, len[:value]
+
+ GC.start(full_mark: false)
+
+ return data
end
- # Turn progress signalling on and off.
+ # Turn progress signalling on and off.
#
# If this is on, the most-downstream image from this image will issue
- # progress signals.
+ # progress signals.
#
# @see Object#signal_connect
# @param state [Boolean] progress signalling state
def set_progress state
- Vips::vips_image_set_progress self, 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
+ Vips.vips_image_set_kill self, kill
end
# Get the `GType` of a metadata field. The result is 0 if no such field
# exists.
#
@@ -566,16 +713,16 @@
# @param name [String] Metadata field to fetch
# @return [Integer] GType
def get_typeof name
# on libvips before 8.5, property types must be searched first,
# since vips_image_get_typeof returned built-in enums as int
- unless Vips::at_least_libvips?(8, 5)
+ unless Vips.at_least_libvips?(8, 5)
gtype = parent_get_typeof name
return gtype if gtype != 0
end
- Vips::vips_image_get_typeof self, name
+ Vips.vips_image_get_typeof self, name
end
# Get a metadata item from an image. Ruby types are constructed
# automatically from the `GValue`, if possible.
#
@@ -590,16 +737,16 @@
# @param name [String] Metadata field to get
# @return [Object] Value of field
def get name
# with old libvips, we must fetch properties (as opposed to
# metadata) via VipsObject
- unless Vips::at_least_libvips?(8, 5)
+ unless Vips.at_least_libvips?(8, 5)
return super if parent_get_typeof(name) != 0
end
gvalue = GObject::GValue.alloc
- raise Vips::Error if Vips::vips_image_get(self, name, gvalue) != 0
+ raise Vips::Error if Vips.vips_image_get(self, name, gvalue) != 0
result = gvalue.get
gvalue.unset
result
end
@@ -610,102 +757,101 @@
# @return [[String]] array of field names
def get_fields
# vips_image_get_fields() was added in libvips 8.5
return [] unless Vips.respond_to? :vips_image_get_fields
- array = Vips::vips_image_get_fields self
+ array = Vips.vips_image_get_fields self
names = []
p = array
until (q = p.read_pointer).null?
names << q.read_string
- GLib::g_free q
+ GLib.g_free q
p += FFI::Type::POINTER.size
end
- GLib::g_free array
+ GLib.g_free array
names
end
- # Create a metadata item on an image of the specifed type. Ruby types
- # are automatically transformed into the matching `GType`, if possible.
+ # Mutate an image with a block. Inside the block, you can call methods
+ # which modify the image, such as setting or removing metadata, or
+ # modifying pixels.
#
- # For example, you can use this to set an image's ICC profile:
+ # For example:
#
+ # ```ruby
+ # image = image.mutate do |x|
+ # (0 ... 1).step(0.01) do |i|
+ # x.draw_line! 255, x.width * i, 0, 0, x.height * (1 - i)
+ # end
+ # end
# ```
- # x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
- # ```
#
- # where `profile` is an ICC profile held as a binary string object.
+ # See {MutableImage}.
+ def mutate
+ mutable = Vips::MutableImage.new self
+ yield mutable
+ mutable.image
+ end
+
+ # This method is deprecated.
#
- # @see set
- # @param gtype [Integer] GType of item
- # @param name [String] Metadata field to set
- # @param value [Object] Value to set
+ # Please use {MutableImage#set_type!} instead.
def set_type gtype, name, value
gvalue = GObject::GValue.alloc
gvalue.init gtype
gvalue.set value
- Vips::vips_image_set self, name, gvalue
+ 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.
+ # This method is deprecated.
#
- # For example, you can use this to set an image's ICC profile:
- #
- # ```
- # x = y.set "icc-profile-data", profile
- # ```
- #
- # where `profile` is an ICC profile held as a binary string object.
- #
- # @see set_type
- # @param name [String] Metadata field to set
- # @param value [Object] Value to set
+ # Please use {MutableImage#set!} instead.
def set name, value
set_type get_typeof(name), name, value
end
- # Remove a metadata item from an image.
+ # This method is deprecated.
#
- # @param name [String] Metadata field to remove
+ # Please use {MutableImage#remove!} instead.
def remove name
- Vips::vips_image_remove self, name
+ Vips.vips_image_remove self, name
end
# compatibility: old name for get
def get_value name
get name
end
- # compatibility: old name for set
+ # This method is deprecated.
+ #
+ # Please use {MutableImage#set!} instead.
def set_value name, value
set name, value
end
# Get image width, in pixels.
#
# @return [Integer] image width, in pixels
def width
- get "width"
+ Vips.vips_image_get_width self
end
# Get image height, in pixels.
#
# @return [Integer] image height, in pixels
def height
- get "height"
+ Vips.vips_image_get_height self
end
# Get number of image bands.
#
# @return [Integer] number of image bands
def bands
- get "bands"
+ Vips.vips_image_get_bands self
end
# Get image format.
#
# @return [Symbol] image format
@@ -785,27 +931,27 @@
# @return [Integer, Integer] image width and height
def size
[width, height]
end
- if Vips::at_least_libvips?(8, 5)
+ if Vips.at_least_libvips?(8, 5)
# Detect if image has an alpha channel
#
# @return [Boolean] true if image has an alpha channel.
def has_alpha?
- return Vips::vips_image_hasalpha(self) != 0
+ Vips.vips_image_hasalpha(self) != 0
end
end
# vips_addalpha was added in libvips 8.6
- if Vips::at_least_libvips?(8, 6)
+ if Vips.at_least_libvips?(8, 6)
# Append an alpha channel to an image.
#
# @return [Image] new image
def add_alpha
ptr = GenericPtr.new
- result = Vips::vips_addalpha self, ptr
+ result = Vips.vips_addalpha self, ptr
raise Vips::Error if result != 0
Vips::Image.new ptr[:value]
end
end
@@ -816,21 +962,21 @@
# memory for large images. See {Image#tilecache} for a way of caching
# parts of an image.
#
# @return [Image] new memory image
def copy_memory
- new_image = Vips::vips_image_copy_memory self
+ new_image = Vips.vips_image_copy_memory self
Vips::Image.new new_image
end
# Draw a point on an image.
#
# See {Image#draw_rect}.
#
# @return [Image] modified image
def draw_point ink, left, top, **opts
- draw_rect ink, left, top, 1, 1, opts
+ draw_rect ink, left, top, 1, 1, **opts
end
# Add an image, constant or array.
#
# @param other [Image, Real, Array<Real>] Thing to add to self
@@ -844,11 +990,11 @@
#
# @param other [Image, Real, Array<Real>] Thing to subtract from self
# @return [Image] result of subtraction
def - other
other.is_a?(Vips::Image) ?
- subtract(other) : linear(1, Image::smap(other) { |x| x * -1 })
+ subtract(other) : linear(1, Image.smap(other) { |x| x * -1 })
end
# Multiply an image, constant or array.
#
# @param other [Image, Real, Array<Real>] Thing to multiply by self
@@ -862,11 +1008,11 @@
#
# @param other [Image, Real, Array<Real>] Thing to divide self by
# @return [Image] result of division
def / other
other.is_a?(Vips::Image) ?
- divide(other) : linear(Image::smap(other) { |x| 1.0 / x }, 0)
+ divide(other) : linear(Image.smap(other) { |x| 1.0 / x }, 0)
end
# Remainder after integer division with an image, constant or array.
#
# @param other [Image, Real, Array<Real>] self modulo this
@@ -988,11 +1134,11 @@
#
# @param other [nil, Image, Real, Array<Real>] test equality to this
# @return [Image] result of equality
def == other
# for equality, we must allow tests against nil
- if other == nil
+ if other.nil?
false
else
call_enum "relational", other, :equal
end
end
@@ -1001,11 +1147,11 @@
#
# @param other [nil, Image, Real, Array<Real>] test inequality to this
# @return [Image] result of inequality
def != other
# for equality, we must allow tests against nil
- if other == nil
+ if other.nil?
true
else
call_enum "relational", other, :noteq
end
end
@@ -1023,42 +1169,44 @@
else
raise Vips::Error, "[] index is not range or numeric."
end
end
- # Convert to an Array. This will be slow for large images.
+ # Convert to an Enumerator. Similar to `#to_a` but lazier.
#
- # @return [Array] array of Fixnum
- def to_a
- # we render the image to a big string, then unpack
- # as a Ruby array of the correct type
- memory = write_to_memory
-
+ # @return [Enumerator] Enumerator of Enumerators of Arrays of Numerics
+ def to_enum
# make the template for unpack
template = {
- char: 'c',
- uchar: 'C',
- short: 's_',
- ushort: 'S_',
- int: 'i_',
- uint: 'I_',
- float: 'f',
- double: 'd',
- complex: 'f',
- dpcomplex: 'd'
- }[format] + '*'
+ char: "c",
+ uchar: "C",
+ short: "s_",
+ ushort: "S_",
+ int: "i_",
+ uint: "I_",
+ float: "f",
+ double: "d",
+ complex: "f",
+ dpcomplex: "d"
+ }[format] + "*"
- # and unpack into something like [1, 2, 3, 4 ..]
- array = memory.unpack(template)
+ # we render the image to a big string, then unpack into
+ # one-dimensional array as a Ruby array of the correct type
+ array = write_to_memory.unpack template
- # gather band elements together
- pixel_array = array.each_slice(bands).to_a
+ # gather bands of a pixel together
+ pixel_array = array.each_slice bands
- # build rows
- row_array = pixel_array.each_slice(width).to_a
+ # gather pixels of a row together
+ pixel_array.each_slice width
+ end
- return row_array
+ # Convert to an Array. This will be slow for large images.
+ #
+ # @return [Array] Array of Arrays of Arrays of Numerics
+ def to_a
+ to_enum.to_a
end
# Return the largest integral value not greater than the argument.
#
# @return [Image] floor of image
@@ -1130,11 +1278,14 @@
# Composite a set of images with a set of blend modes.
#
# @param overlay [Image, Array<Image>] images to composite
# @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 [Array<Integer>] :x x positions of overlay
+ # @option opts [Array<Integer>] :y y positions of overlay
+ # @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, **options
unless overlay.is_a? Array
overlay = [overlay]
@@ -1154,32 +1305,32 @@
#
# @return [Real, Real, Real] maximum value, x coordinate of maximum, y
# coordinate of maximum
def maxpos
v, opts = max x: true, y: true
- x = opts['x']
- y = opts['y']
- return v, x, y
+ x = opts["x"]
+ y = opts["y"]
+ [v, x, y]
end
# Return the coordinates of the image minimum.
#
# @return [Real, Real, Real] minimum value, x coordinate of minimum, y
# coordinate of minimum
def minpos
v, opts = min x: true, y: true
- x = opts['x']
- y = opts['y']
- return v, x, y
+ x = opts["x"]
+ y = opts["y"]
+ [v, x, y]
end
# a median filter
#
# @param size [Integer] size of filter window
# @return [Image] result of median filter
def median size = 3
- rank size, size, (size * size) / 2
+ rank size, size, size**2 / 2
end
# Return the real part of a complex image.
#
# @return [Image] real part of complex image
@@ -1202,11 +1353,11 @@
# bands are treated as (x, y) coordinates.
#
# @see xyz
# @return [Image] image converted to polar coordinates
def polar
- Image::run_cmplx(self) { |x| x.complex :polar }
+ Image.run_cmplx(self) { |x| x.complex :polar }
end
# Return an image with polar pixels converted to rectangular.
#
# The image
@@ -1215,11 +1366,11 @@
# bands are treated as (x, y) coordinates.
#
# @see xyz
# @return [Image] image converted to rectangular coordinates
def rect
- Image::run_cmplx(self) { |x| x.complex :rect }
+ Image.run_cmplx(self) { |x| x.complex :rect }
end
# Return the complex conjugate of an image.
#
# The image
@@ -1227,11 +1378,11 @@
# or must have an even number of bands, in which case pairs of
# bands are treated as (x, y) coordinates.
#
# @return [Image] complex conjugate
def conj
- Image::run_cmplx(self) { |x| x.complex :conj }
+ Image.run_cmplx(self) { |x| x.complex :conj }
end
# Calculate the cross phase of two images.
#
# @param other [Image, Real, Array<Real>] cross phase with this
@@ -1280,10 +1431,52 @@
# @return [Image] inverse tangent of each pixel
def atan
math :atan
end
+ # Return the hyperbolic sine of an image in radians.
+ #
+ # @return [Image] sine of each pixel
+ def sinh
+ math :sinh
+ end
+
+ # Return the hyperbolic cosine of an image in radians.
+ #
+ # @return [Image] cosine of each pixel
+ def cosh
+ math :cosh
+ end
+
+ # Return the hyperbolic tangent of an image in radians.
+ #
+ # @return [Image] tangent of each pixel
+ def tanh
+ math :tanh
+ end
+
+ # Return the inverse hyperbolic sine of an image in radians.
+ #
+ # @return [Image] inverse sine of each pixel
+ def asinh
+ math :asinh
+ end
+
+ # Return the inverse hyperbolic cosine of an image in radians.
+ #
+ # @return [Image] inverse cosine of each pixel
+ def acosh
+ math :acosh
+ end
+
+ # Return the inverse hyperbolic tangent of an image in radians.
+ #
+ # @return [Image] inverse tangent of each pixel
+ def atanh
+ math :atanh
+ end
+
# Return the natural log of an image.
#
# @return [Image] natural log of each pixel
def log
math :log
@@ -1408,11 +1601,11 @@
#
# Regenerate with something like:
#
# ```
# $ ruby > methods.rb
- # require 'vips'; Vips::Yard.generate
+ # require "vips"; Vips::Yard.generate
# ^D
# ```
module Yard
# map gobject's type names to Ruby
@@ -1430,30 +1623,30 @@
"VipsSourceCustom" => "Vips::SourceCustom",
"VipsTargetCustom" => "Vips::TargetCustom",
"VipsArrayDouble" => "Array<Double>",
"VipsArrayInt" => "Array<Integer>",
"VipsArrayImage" => "Array<Image>",
- "VipsArrayString" => "Array<String>",
+ "VipsArrayString" => "Array<String>"
}
# these have hand-written methods, see above
NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
# these are aliased (appear under several names)
ALIAS = ["crop"]
# 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
+ 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
+ fundamental == GObject::GENUM_TYPE
type_name = "Vips::" + type_name[/Vips(.*)/, 1]
end
type_name
end
@@ -1462,17 +1655,17 @@
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
+ optional_input = introspect.doc_optional_input
+ optional_output = introspect.doc_optional_output
print "# @!method "
print "self." unless introspect.member_x
print "#{introspect.name}("
- print method_args.map{ |x| x[:yard_name] }.join(", ")
+ print method_args.map { |x| x[:yard_name] }.join(", ")
print ", " if method_args.length > 0
puts "**opts)"
puts "# #{introspect.description.capitalize}."
@@ -1486,39 +1679,39 @@
puts "# @param opts [Hash] Set of options"
optional_input.each do |arg_name, details|
yard_name = details[:yard_name]
gtype = details[:gtype]
+ rtype = gtype_to_ruby gtype
blurb = details[:blurb]
- puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} " +
- "#{blurb}"
+ puts "# @option opts [#{rtype}] :#{yard_name} #{blurb}"
end
optional_output.each do |arg_name, details|
yard_name = details[:yard_name]
gtype = details[:gtype]
+ rtype = gtype_to_ruby gtype
blurb = details[:blurb]
- print "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name}"
- puts " Output #{blurb}"
+ puts "# @option opts [#{rtype}] :#{yard_name} Output #{blurb}"
end
print "# @return ["
if required_output.length == 0
print "nil"
elsif required_output.length == 1
print gtype_to_ruby(required_output.first[:gtype])
else
print "Array<"
- print required_output.map{ |x| gtype_to_ruby(x[:gtype]) }.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 ""
@@ -1526,20 +1719,20 @@
puts ""
end
def self.generate
alias_gtypes = {}
- ALIAS.each do |name|
- gtype = Vips::type_find "VipsOperation", name
- alias_gtypes[gtype] = name
+ ALIAS.each do |name|
+ gtype = Vips.type_find "VipsOperation", name
+ alias_gtypes[gtype] = name
end
generate_class = lambda do |gtype, _|
- if alias_gtypes.key? gtype
- name = alias_gtypes[gtype]
+ name = if alias_gtypes.key? gtype
+ alias_gtypes[gtype]
else
- name = Vips::nickname_find gtype
+ Vips.nickname_find gtype
end
if name
begin
# can fail for abstract types
@@ -1549,17 +1742,17 @@
end
generate_operation(introspect) if introspect
end
- Vips::vips_type_map gtype, generate_class, nil
+ Vips.vips_type_map gtype, generate_class, nil
end
puts "module Vips"
puts " class Image"
puts ""
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
+ generate_class.call(GObject.g_type_from_name("VipsOperation"), nil)
puts " end"
puts "end"
end
end