from __future__ import division
from __future__ import unicode_literals
import logging
import numbers
import sys
import pyvips
from pyvips import ffi, vips_lib, gobject_lib, \
glib_lib, Error, _to_bytes, _to_string, type_name, type_from_name, \
at_least_libvips
logger = logging.getLogger(__name__)
_is_PY2 = sys.version_info.major == 2
[docs]class GValue(object):
"""Wrap GValue in a Python class.
This class wraps :class:`.GValue` in a convenient interface. You can use
instances of this class to get and set :class:`.GObject` properties.
On construction, :class:`.GValue` is all zero (empty). You can pass it to
a get function to have it filled by :class:`.GObject`, or use init to
set a type, set to set a value, then use it to set an object property.
GValue lifetime is managed automatically.
"""
__slots__ = ('pointer', 'gvalue')
# look up some common gtypes at init for speed
gbool_type = type_from_name('gboolean')
gint_type = type_from_name('gint')
guint64_type = type_from_name('guint64')
gdouble_type = type_from_name('gdouble')
gstr_type = type_from_name('gchararray')
genum_type = type_from_name('GEnum')
gflags_type = type_from_name('GFlags')
gobject_type = type_from_name('GObject')
image_type = type_from_name('VipsImage')
array_int_type = type_from_name('VipsArrayInt')
array_double_type = type_from_name('VipsArrayDouble')
array_image_type = type_from_name('VipsArrayImage')
refstr_type = type_from_name('VipsRefString')
blob_type = type_from_name('VipsBlob')
source_type = type_from_name('VipsSource')
target_type = type_from_name('VipsTarget')
pyvips.vips_lib.vips_band_format_get_type()
format_type = type_from_name('VipsBandFormat')
if at_least_libvips(8, 6):
pyvips.vips_lib.vips_blend_mode_get_type()
blend_mode_type = type_from_name('VipsBlendMode')
# map a gtype to the name of the corresponding Python type
_gtype_to_python = {
gbool_type: 'bool',
gint_type: 'int',
guint64_type: 'long', # Note: int and long are unified in Python 3
gdouble_type: 'float',
gstr_type: 'str',
refstr_type: 'str',
genum_type: 'str',
gflags_type: 'int',
gobject_type: 'GObject',
image_type: 'Image',
array_int_type: 'list[int]',
array_double_type: 'list[float]',
array_image_type: 'list[Image]',
blob_type: 'str',
source_type: 'Source',
target_type: 'Target',
}
[docs] @staticmethod
def gtype_to_python(gtype):
"""Map a gtype to the name of the Python type we use to represent it.
"""
fundamental = gobject_lib.g_type_fundamental(gtype)
# enums can be strings or class members ... we want to generate a union
# type
if fundamental == GValue.genum_type:
name = type_name(gtype)
if name.startswith('Vips'):
name = name[4:]
return "Union[str, %s]" % name
if gtype in GValue._gtype_to_python:
return GValue._gtype_to_python[gtype]
if fundamental in GValue._gtype_to_python:
return GValue._gtype_to_python[fundamental]
return '<unknown type>'
[docs] @staticmethod
def to_enum(gtype, value):
"""Turn a string into an enum value ready to be passed into libvips.
"""
if isinstance(value, basestring if _is_PY2 else str):
enum_value = vips_lib.vips_enum_from_nick(b'pyvips', gtype,
_to_bytes(value))
if enum_value < 0:
raise Error('no value {0} in gtype {1} ({2})'.
format(value, type_name(gtype), gtype))
else:
enum_value = value
return enum_value
[docs] @staticmethod
def from_enum(gtype, enum_value):
"""Turn an int back into an enum string.
"""
pointer = vips_lib.vips_enum_nick(gtype, enum_value)
if pointer == ffi.NULL:
raise Error('value not in enum')
return _to_string(pointer)
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)
# logger.debug('GValue.__init__: gvalue = %s', self.gvalue)
[docs] def set_type(self, gtype):
"""Set the type of a GValue.
GValues have a set type, fixed at creation time. Use set_type to set
the type of a GValue before assigning to it.
GTypes are 32 or 64-bit integers (depending on the platform). See
type_find.
"""
gobject_lib.g_value_init(self.gvalue, gtype)
[docs] 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.guint64_type:
gobject_lib.g_value_set_uint64(self.gvalue, 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:
# we declare the type of the free func in set_blob incorrectly
# so that we can pass g_free at runtime without triggering an
# exception
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)))
[docs] 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.guint64_type:
result = gobject_lib.g_value_get_uint64(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(vi)
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
__all__ = ['GValue']