def __init__(self): # allocate memory for the gvalue which will be freed on GC self.pointer = ffi.new('GValue *') # logger.debug('GValue.__init__: pointer = %s', self.pointer) # and tag it to be unset on GC as well self.gvalue = ffi.gc(self.pointer, gobject_lib.g_value_unset)
def write_to_memory(self): """Write the image to a large memory array. A large area of memory is allocated, the image is rendered to that memory array, and the array is returned as a buffer. For example, if you have a 2x2 uchar image containing the bytes 1, 2, 3, 4, read left-to-right, top-to-bottom, then:: buf = image.write_to_memory() will return a four byte buffer containing the values 1, 2, 3, 4. Returns: buffer Raises: :class:`.Error` """ psize = ffi.new('size_t *') pointer = vips_lib.vips_image_write_to_memory(self.pointer, psize) if pointer == ffi.NULL: raise Error('unable to write to memory') pointer = ffi.gc(pointer, glib_lib.g_free) return ffi.buffer(pointer, psize[0])
def _get_pspec(self, name): # logger.debug('VipsObject.get_typeof: self = %s, name = %s', # str(self), name) pspec = ffi.new('GParamSpec **') argument_class = ffi.new('VipsArgumentClass **') argument_instance = ffi.new('VipsArgumentInstance **') result = vips_lib.vips_object_get_argument(self.vobject, _to_bytes(name), pspec, argument_class, argument_instance) if result != 0: return None return pspec[0]
def new_from_array(array, scale=1.0, offset=0.0): """Create an image from a 1D or 2D array. A new one-band image with :class:`BandFormat` ``'double'`` pixels is created from the array. These image are useful with the libvips convolution operator :meth:`Image.conv`. Args: array (list[list[float]]): Create the image from these values. 1D arrays become a single row of pixels. scale (float): Default to 1.0. What to divide each pixel by after convolution. Useful for integer convolution masks. offset (float): Default to 0.0. What to subtract from each pixel after convolution. Useful for integer convolution masks. Returns: A new :class:`Image`. Raises: :class:`.Error` """ if not _is_2D(array): array = [array] height = len(array) width = len(array[0]) n = width * height a = ffi.new('double[]', n) for y in range(0, height): for x in range(0, width): a[x + y * width] = array[y][x] vi = vips_lib.vips_image_new_matrix_from_array(width, height, a, n) if vi == ffi.NULL: raise Error('unable to make image from matrix') image = pyvips.Image(vi) image.set_type(GValue.gdouble_type, 'scale', scale) image.set_type(GValue.gdouble_type, 'offset', offset) return image
def fetch(self, x, y, w, h): """Fill a region with pixel data. Pixels are filled with data! Returns: Pixel data. Raises: :class:`.Error` """ if not at_least_libvips(8, 8): raise Error('libvips too old') psize = ffi.new('size_t *') pointer = vips_lib.vips_region_fetch(self.pointer, x, y, w, h, psize) if pointer == ffi.NULL: raise Error('unable to fetch from region') pointer = ffi.gc(pointer, glib_lib.g_free) return ffi.buffer(pointer, psize[0])
def __init__(self, operation_name): op = Operation.new_from_name(operation_name) self.description = op.get_description() self.flags = vips_lib.vips_operation_get_flags(op.pointer) # build a list of constructor arg [name, flags] pairs in arg order arguments = [] def add_args(name, flags): if (flags & _CONSTRUCT) != 0: # libvips uses '-' to separate parts of arg names, but we # need '_' for Python name = name.replace('-', '_') arguments.append([name, flags]) if at_least_libvips(8, 7): p_names = ffi.new('char**[1]') p_flags = ffi.new('int*[1]') p_n_args = ffi.new('int[1]') result = vips_lib.vips_object_get_args(op.vobject, p_names, p_flags, p_n_args) if result != 0: raise Error('unable to get arguments from operation') p_names = p_names[0] p_flags = p_flags[0] n_args = p_n_args[0] for i in range(0, n_args): add_args(_to_string(p_names[i]), p_flags[i]) else: def add_construct(self, pspec, argument_class, argument_instance, a, b): add_args(_to_string(pspec.name), argument_class.flags) return ffi.NULL cb = ffi.callback('VipsArgumentMapFn', add_construct) vips_lib.vips_argument_map(op.vobject, cb, ffi.NULL, ffi.NULL) # logger.debug('arguments = %s', self.arguments) # build a hash from arg name to detailed arg information self.details = {} for name, flags in arguments: self.details[name] = { "name": name, "flags": flags, "blurb": op.get_blurb(name), "type": op.get_typeof(name) } # lists of arg names by category self.required_input = [] self.optional_input = [] self.required_output = [] self.optional_output = [] for name, flags in arguments: if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0 and (flags & _DEPRECATED) == 0): self.required_input.append(name) # required inputs which we MODIFY are also required outputs if (flags & _MODIFY) != 0: self.required_output.append(name) if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) != 0 and (flags & _DEPRECATED) == 0): self.required_output.append(name) # we let deprecated optional args through, but warn about them # if they get used, see below if ((flags & _INPUT) != 0 and (flags & _REQUIRED) == 0): self.optional_input.append(name) if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) == 0): self.optional_output.append(name) # find the first required input image arg, if any ... that will be self self.member_x = None for name in self.required_input: details = self.details[name] if details['type'] == GValue.image_type: self.member_x = name break # method args are required args, but without the image they are a # method on if self.member_x is not None: self.method_args = list(self.required_input) self.method_args.remove(self.member_x) else: self.method_args = self.required_input
def get(self): """Get the contents of a GValue. The contents of the GValue are read out as a Python type. """ # logger.debug('GValue.get: self = %s', self) gtype = self.gvalue.g_type fundamental = gobject_lib.g_type_fundamental(gtype) result = None if gtype == GValue.gbool_type: result = bool(gobject_lib.g_value_get_boolean(self.gvalue)) elif gtype == GValue.gint_type: result = gobject_lib.g_value_get_int(self.gvalue) elif gtype == GValue.gdouble_type: result = gobject_lib.g_value_get_double(self.gvalue) elif fundamental == GValue.genum_type: return GValue.from_enum(gtype, gobject_lib.g_value_get_enum(self.gvalue)) elif fundamental == GValue.gflags_type: result = gobject_lib.g_value_get_flags(self.gvalue) elif gtype == GValue.gstr_type: pointer = gobject_lib.g_value_get_string(self.gvalue) if pointer != ffi.NULL: result = _to_string(pointer) elif gtype == GValue.refstr_type: psize = ffi.new('size_t *') pointer = vips_lib.vips_value_get_ref_string(self.gvalue, psize) # psize[0] will be number of bytes in string, but just assume it's # NULL-terminated result = _to_string(pointer) elif gtype == GValue.image_type: # g_value_get_object() will not add a ref ... that is # held by the gvalue go = gobject_lib.g_value_get_object(self.gvalue) vi = ffi.cast('VipsImage *', go) # we want a ref that will last with the life of the vimage: # this ref is matched by the unref that's attached to finalize # by Image() gobject_lib.g_object_ref(go) result = pyvips.Image(vi) elif gtype == GValue.array_int_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_int(self.gvalue, pint) result = [] for i in range(0, pint[0]): result.append(array[i]) elif gtype == GValue.array_double_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_double(self.gvalue, pint) result = [] for i in range(0, pint[0]): result.append(array[i]) elif gtype == GValue.array_image_type: pint = ffi.new('int *') array = vips_lib.vips_value_get_array_image(self.gvalue, pint) result = [] for i in range(0, pint[0]): vi = array[i] gobject_lib.g_object_ref(vi) image = pyvips.Image(vi) result.append(image) elif gtype == GValue.blob_type: psize = ffi.new('size_t *') array = vips_lib.vips_value_get_blob(self.gvalue, psize) buf = ffi.cast('char*', array) result = ffi.unpack(buf, psize[0]) else: raise Error('unsupported gtype for get {0}'.format( type_name(gtype))) return result
def set(self, value): """Set a GValue. The value is converted to the type of the GValue, if possible, and assigned. """ # logger.debug('GValue.set: value = %s', value) gtype = self.gvalue.g_type fundamental = gobject_lib.g_type_fundamental(gtype) if gtype == GValue.gbool_type: gobject_lib.g_value_set_boolean(self.gvalue, value) elif gtype == GValue.gint_type: gobject_lib.g_value_set_int(self.gvalue, int(value)) elif gtype == GValue.gdouble_type: gobject_lib.g_value_set_double(self.gvalue, value) elif fundamental == GValue.genum_type: gobject_lib.g_value_set_enum(self.gvalue, GValue.to_enum(gtype, value)) elif fundamental == GValue.gflags_type: gobject_lib.g_value_set_flags(self.gvalue, value) elif gtype == GValue.gstr_type: gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value)) elif gtype == GValue.refstr_type: vips_lib.vips_value_set_ref_string(self.gvalue, _to_bytes(value)) elif fundamental == GValue.gobject_type: gobject_lib.g_value_set_object(self.gvalue, value.pointer) elif gtype == GValue.array_int_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('int[]', value) vips_lib.vips_value_set_array_int(self.gvalue, array, len(value)) elif gtype == GValue.array_double_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('double[]', value) vips_lib.vips_value_set_array_double(self.gvalue, array, len(value)) elif gtype == GValue.array_image_type: if isinstance(value, pyvips.Image): value = [value] vips_lib.vips_value_set_array_image(self.gvalue, len(value)) array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL) for i, image in enumerate(value): gobject_lib.g_object_ref(image.pointer) array[i] = image.pointer elif gtype == GValue.blob_type: # we need to set the blob to a copy of the string that vips_lib # can own memory = glib_lib.g_malloc(len(value)) ffi.memmove(memory, value, len(value)) # this is horrible! # # * in API mode, we must have 8.6+ and use set_blob_free to # attach the metadata to avoid leaks # * pre-8.6, we just pass a NULL free pointer and live with the # leak # # this is because in API mode you can't pass a builtin (what # vips_lib.g_free() becomes) as a parameter to ffi.callback(), and # vips_value_set_blob() needs a callback for arg 2 # # additionally, you can't make a py def which calls g_free() and # then use the py def in the callback, since libvips will trigger # these functions during cleanup, and py will have shut down by # then and you'll get a segv if at_least_libvips(8, 6): vips_lib.vips_value_set_blob_free(self.gvalue, memory, len(value)) else: if pyvips.API_mode: vips_lib.vips_value_set_blob(self.gvalue, ffi.NULL, memory, len(value)) else: vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free, memory, len(value)) else: raise Error( 'unsupported gtype for set {0}, fundamental {1}'.format( type_name(gtype), type_name(fundamental)))
def set(self, value): """Set a GValue. The value is converted to the type of the GValue, if possible, and assigned. """ # logger.debug('GValue.set: value = %s', value) gtype = self.gvalue.gtype fundamental = gobject_lib.g_type_fundamental(gtype) if gtype == GValue.gbool_type: gobject_lib.g_value_set_boolean(self.gvalue, value) elif gtype == GValue.gint_type: gobject_lib.g_value_set_int(self.gvalue, int(value)) elif gtype == GValue.gdouble_type: gobject_lib.g_value_set_double(self.gvalue, value) elif fundamental == GValue.genum_type: gobject_lib.g_value_set_enum(self.gvalue, GValue.to_enum(gtype, value)) elif fundamental == GValue.gflags_type: gobject_lib.g_value_set_flags(self.gvalue, value) elif gtype == GValue.gstr_type or gtype == GValue.refstr_type: gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value)) elif fundamental == GValue.gobject_type: gobject_lib.g_value_set_object(self.gvalue, value.pointer) elif gtype == GValue.array_int_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('int[]', value) vips_lib.vips_value_set_array_int(self.gvalue, array, len(value)) elif gtype == GValue.array_double_type: if isinstance(value, numbers.Number): value = [value] array = ffi.new('double[]', value) vips_lib.vips_value_set_array_double(self.gvalue, array, len(value)) elif gtype == GValue.array_image_type: if isinstance(value, pyvips.Image): value = [value] vips_lib.vips_value_set_array_image(self.gvalue, len(value)) array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL) for i, image in enumerate(value): gobject_lib.g_object_ref(image.pointer) array[i] = image.pointer elif gtype == GValue.blob_type: # we need to set the blob to a copy of the string that vips_lib # can own memory = glib_lib.g_malloc(len(value)) ffi.memmove(memory, value, len(value)) vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free, memory, len(value)) else: raise Error( 'unsupported gtype for set {0}, fundamental {1}'.format( type_name(gtype), type_name(fundamental)))