예제 #1
0
    def call(operation_name, *args, **kwargs):
        """Call a libvips operation.

        Use this method to call any libvips operation. For example::

            black_image = pyvips.Operation.call('black', 10, 10)

        See the Introduction for notes on how this works.

        """

        logger.debug('VipsOperation.call: operation_name = %s', operation_name)
        # logger.debug('VipsOperation.call: args = %s, kwargs =%s',
        #              args, kwargs)

        # pull out the special string_options kwarg
        string_options = kwargs.pop('string_options', '')

        logger.debug('VipsOperation.call: string_options = %s', string_options)

        op = Operation.new_from_name(operation_name)

        arguments = op.get_args()
        # logger.debug('arguments = %s', arguments)

        # make a thing to quickly get flags from an arg name
        flags_from_name = {}

        # count required input args
        n_required = 0

        for name, flags in arguments:
            flags_from_name[name] = flags

            if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0
                    and (flags & _DEPRECATED) == 0):
                n_required += 1

        if n_required != len(args):
            raise Error('unable to call {0}: {1} arguments given, '
                        'but {2} required'.format(operation_name, len(args),
                                                  n_required))

        # the first image argument is the thing we expand constants to
        # match ... look inside tables for images, since we may be passing
        # an array of image as a single param
        match_image = _find_inside(lambda x: isinstance(x, pyvips.Image), args)

        logger.debug('VipsOperation.call: match_image = %s', match_image)

        # set any string options before any args so they can't be
        # overridden
        if not op.set_string(string_options):
            raise Error('unable to call {0}'.format(operation_name))

        # set required and optional args
        n = 0
        for name, flags in arguments:
            if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0
                    and (flags & _DEPRECATED) == 0):
                op.set(name, flags, match_image, args[n])
                n += 1

        for name, value in kwargs.items():
            if name not in flags_from_name:
                raise Error('{0} does not support argument '
                            '{1}'.format(operation_name, name))

            op.set(name, flags_from_name[name], match_image, value)

        # build operation
        vop = vips_lib.vips_cache_operation_build(op.pointer)
        if vop == ffi.NULL:
            raise Error('unable to call {0}'.format(operation_name))
        op = Operation(vop)

        # fetch required output args, plus modified input images
        result = []
        for name, flags in arguments:
            if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) != 0
                    and (flags & _DEPRECATED) == 0):
                result.append(op.get(name))

            if (flags & _INPUT) != 0 and (flags & _MODIFY) != 0:
                result.append(op.get(name))

        # fetch optional output args
        opts = {}
        for name, value in kwargs.items():
            flags = flags_from_name[name]

            if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) == 0
                    and (flags & _DEPRECATED) == 0):
                opts[name] = op.get(name)

        vips_lib.vips_object_unref_outputs(op.object)

        if len(opts) > 0:
            result.append(opts)

        if len(result) == 0:
            result = None
        elif len(result) == 1:
            result = result[0]

        logger.debug('VipsOperation.call: result = %s', result)

        return result
예제 #2
0
    def call(operation_name, *args, **kwargs):
        """Call a libvips operation.

        Use this method to call any libvips operation. For example::

            black_image = pyvips.Operation.call('black', 10, 10)

        See the Introduction for notes on how this works.

        """

        logger.debug('VipsOperation.call: operation_name = %s', operation_name)
        # logger.debug('VipsOperation.call: args = %s, kwargs =%s',
        #              args, kwargs)

        intro = Introspect.get(operation_name)

        if len(intro.required_input) != len(args):
            raise Error('{0} needs {1} arguments, but {2} given'.format(
                operation_name, len(intro.required_input), len(args)))

        op = Operation.new_from_name(operation_name)

        # set any string options before any args so they can't be
        # overridden
        string_options = kwargs.pop('string_options', '')
        if not op.set_string(string_options):
            raise Error('unable to call {0}'.format(operation_name))

        # the first image argument is the thing we expand constants to
        # match ... look inside tables for images, since we may be passing
        # an array of images as a single param
        match_image = _find_inside(lambda x: isinstance(x, pyvips.Image), args)

        logger.debug('VipsOperation.call: match_image = %s', match_image)

        # collect a list of all input references here
        # we can't use a set because set elements are unique under "==", and
        # Python checks memoryview equality with hash functions, not pointer
        # equality
        references = []

        # does a list contain an element using pointer equality
        # we can't use "in" since that uses "==", which means hash equality
        def contains(array, x):
            for y in array:
                if id(x) == id(y):
                    return True
            return False

        def add_reference(x):
            if isinstance(x, pyvips.Image):
                for i in x._references:
                    if not contains(references, i):
                        references.append(i)
            return False

        # set required input args
        for name, value in zip(intro.required_input, args):
            _find_inside(add_reference, value)
            op.set(name, intro.details[name]['flags'], match_image, value)

        # set any optional args
        for name in kwargs:
            if (name not in intro.optional_input
                    and name not in intro.optional_output):
                raise Error(
                    '{0} does not support optional argument {1}'.format(
                        operation_name, name))

            value = kwargs[name]
            details = intro.details[name]

            if (details['flags'] & _DEPRECATED) != 0:
                logger.info('{0} argument {1} is deprecated', operation_name,
                            name)

            _find_inside(add_reference, value)
            op.set(name, details['flags'], match_image, value)

        # build operation
        vop = vips_lib.vips_cache_operation_build(op.pointer)
        if vop == ffi.NULL:
            vips_lib.vips_object_unref_outputs(op.vobject)
            raise Error('unable to call {0}'.format(operation_name))
        op = Operation(vop)

        # attach all input refs to output x
        def set_reference(x):
            if isinstance(x, pyvips.Image):
                x._references.extend(references)
            return False

        # fetch required output args (plus modified input images)
        result = []
        for name in intro.required_output:
            value = op.get(name)
            _find_inside(set_reference, value)
            result.append(value)

        # fetch optional output args
        opts = {}
        for name in intro.optional_output:
            if name in kwargs:
                value = op.get(name)
                _find_inside(set_reference, value)
                opts[name] = value

        if len(opts) > 0:
            result.append(opts)

        vips_lib.vips_object_unref_outputs(op.vobject)

        if len(result) == 0:
            result = None
        elif len(result) == 1:
            result = result[0]

        logger.debug('VipsOperation.call: result = %s', result)

        return result