Ejemplo n.º 1
0
    def add_enum(gtype, a, b):
        nickname = type_name(gtype)
        all_enums.append(nickname)

        type_map(gtype, add_enum)

        return ffi.NULL
Ejemplo n.º 2
0
    def add_enum(gtype, a, b):
        nickname = type_name(gtype)
        all_nicknames.append(nickname)
        gtype_to_js_param[gtype] = f'{remove_prefix(nickname)} | Enum'

        type_map(gtype, add_enum)

        return ffi.NULL
Ejemplo n.º 3
0
def generate_operation(operation_name):
    intro = Introspect.get(operation_name)

    result = ' * @method '
    if intro.member_x is None:
        result += 'static '
    if len(intro.required_output) == 0:
        result += 'void '
    elif len(intro.required_output) == 1:
        arg = intro.required_output[0]
        details = intro.details[arg]
        result += '{0} '.format(gtype_to_php(details['type'], True))
    else:
        # we generate a Returns: block for this case, see below
        result += 'array '

    result += '{0}('.format(operation_name)
    for name in intro.method_args:
        details = intro.details[name]
        result += '{0} ${1}, '.format(gtype_to_php(details['type']), name)

    result += 'array $options = []) '

    description = intro.description
    result += description[0].upper() + description[1:] + '.\n'

    # find any Enums we've referenced and output @see lines for them
    for name in intro.required_output + intro.method_args:
        details = intro.details[name]
        fundamental = gobject_lib.g_type_fundamental(details['type'])

        if fundamental != GValue.genum_type:
            continue

        result += ' *     @see {0} for possible values for ${1}\n'.format(
            remove_prefix(type_name(details['type'])), name)

    if len(intro.required_output) > 1:
        result += ' *     Return array with: [\n'
        for name in intro.required_output:
            details = intro.details[name]
            result += ' *         \'{0}\' => @type {1} {2}\n'.format(
                name, gtype_to_php(details['type']),
                details['blurb'][0].upper() + details['blurb'][1:])
        result += ' *     ];\n'

    result += ' *     @throws Exception\n'

    return result
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
def get_cpp_type(gtype):
    """Map a gtype to C++ type name we use to represent it.
    """
    if gtype in gtype_to_cpp:
        return gtype_to_cpp[gtype]

    fundamental = gobject_lib.g_type_fundamental(gtype)

    # enum params use the C name as their name
    if fundamental == GValue.genum_type:
        return type_name(gtype)

    if fundamental in gtype_to_cpp:
        return gtype_to_cpp[fundamental]

    return '<unknown type>'
Ejemplo n.º 6
0
def get_cpp_type(gtype):
    """Map a gtype to C++ type name we use to represent it.
    """
    if gtype in gtype_to_cpp:
        return gtype_to_cpp[gtype]

    fundamental = gobject_lib.g_type_fundamental(gtype)

    # enum params use the C name as their name
    if fundamental == GValue.genum_type:
        return type_name(gtype)

    if fundamental in gtype_to_cpp:
        return gtype_to_cpp[fundamental]

    return '<unknown type>'
Ejemplo n.º 7
0
    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>'
Ejemplo n.º 8
0
    def new_pointer_from_gtype(gtype):
        """Make a new GObject pointer from a gtype.

        This is useful for subclasses which need to control the construction
        process.

        You can pass the result pointer to the Python constructor for the
        object you are building. You will need to call VipsObject.build() to
        finish construction.

        Returns:
            A pointer to a new GObject.

        Raises:
            :class:`.Error`

        """

        pointer = gobject_lib.g_object_new(gtype, ffi.NULL)
        if pointer == ffi.NULL:
            raise Error("can't create {0}".format(type_name(gtype)))

        return pointer
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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)))
Ejemplo n.º 11
0
def generate_operation(operation_name):
    op = Operation.new_from_name(operation_name)

    # we are only interested in non-deprecated args
    args = [[name, flags] for name, flags in op.get_args()
            if not flags & _DEPRECATED]

    # find the first required input image arg, if any ... that will be self
    member_x = None
    for name, flags in args:
        if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0
                and op.get_typeof(name) == GValue.image_type):
            member_x = name
            break

    required_input = [
        name for name, flags in args
        if (flags & _INPUT) != 0 and (flags
                                      & _REQUIRED) != 0 and name != member_x
    ]

    required_output = [
        name for name, flags in args
        if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) != 0) or (
            (flags & _INPUT) != 0 and (flags & _REQUIRED) != 0 and
            (flags & _MODIFY) != 0)
    ]

    result = ' * @method '
    if member_x is None:
        result += 'static '
    if len(required_output) == 0:
        result += 'void '
    elif len(required_output) == 1:
        result += '{0} '.format(
            gtype_to_php(op.get_typeof(required_output[0]), True))
    else:
        # we generate a Returns: block for this case, see below
        result += 'array '

    result += '{0}('.format(operation_name)
    for name in required_input:
        gtype = op.get_typeof(name)
        result += '{0} ${1}, '.format(gtype_to_php(gtype), name)

    result += 'array $options = []) '

    description = op.get_description()
    result += description[0].upper() + description[1:] + '.\n'

    # find any Enums we've referenced and output @see lines for them
    for name in required_output + required_input:
        gtype = op.get_typeof(name)
        fundamental = gobject_lib.g_type_fundamental(gtype)

        if fundamental != GValue.genum_type:
            continue

        result += ' *     @see {0} for possible values for ${1}\n'.format(
            remove_prefix(type_name(gtype)), name)

    if len(required_output) > 1:
        result += ' *     Return array with: [\n'
        for name in required_output:
            gtype = op.get_typeof(name)
            blurb = op.get_blurb(name)
            result += ' *         \'{0}\' => @type {1} {2}\n'.format(
                name, gtype_to_php(gtype), blurb[0].upper() + blurb[1:])
        result += ' *     ];\n'

    result += ' *     @throws Exception\n'

    return result
Ejemplo n.º 12
0
def generate_auto_doc(filename):
    all_nicknames = []

    def add_nickname(gtype, a, b):
        nickname = nickname_find(gtype)
        try:
            # can fail for abstract types
            op = Operation.new_from_name(nickname)

            # we are only interested in non-deprecated operations
            if (op.get_flags() & _OPERATION_DEPRECATED) == 0:
                all_nicknames.append(nickname)
        except Error:
            pass

        type_map(gtype, add_nickname)

        return ffi.NULL

    type_map(type_from_name('VipsOperation'), add_nickname)

    # add 'missing' synonyms by hand
    all_nicknames.append('crop')

    # make list unique and sort
    all_nicknames = list(set(all_nicknames))
    all_nicknames.sort()

    # these have hand-written methods, don't autodoc them
    no_generate = [
        'bandjoin', 'bandrank', 'ifthenelse', 'add', 'subtract', 'multiply',
        'divide', 'remainder'
    ]
    all_nicknames = [x for x in all_nicknames if x not in no_generate]

    print('Generating {0} ...'.format(filename))

    with open(filename, 'w') as f:
        f.write(preamble)
        f.write('\n')
        f.write('namespace Jcupitt\\Vips;\n')
        f.write('\n')
        f.write('/**\n')
        f.write(' * Autodocs for the Image class.\n')
        f.write(class_header)
        f.write(' *\n')

        for nickname in all_nicknames:
            f.write(generate_operation(nickname))

        f.write(' *\n')

        # all magic properties
        tmp_file = Image.new_temp_file('%s.v')
        all_properties = tmp_file.get_fields()
        for name in all_properties:
            php_name = name.replace('-', '_')
            gtype = tmp_file.get_typeof(name)
            fundamental = gobject_lib.g_type_fundamental(gtype)

            f.write(' * @property {0} ${1} {2}\n'.format(
                gtype_to_php(gtype), php_name, tmp_file.get_blurb(name)))

            if fundamental == GValue.genum_type:
                f.write(' *     @see {0} for possible values\n'.format(
                    remove_prefix(type_name(gtype))))

        f.write(' */\n')
        f.write('abstract class ImageAutodoc\n')
        f.write('{\n')
        f.write('}\n')
Ejemplo n.º 13
0
def generate_operation(operation_name, declaration_only=False):
    intro = Introspect.get(operation_name)

    required_output = [
        name for name in intro.required_output if name != intro.member_x
    ]

    has_input = len(intro.method_args) >= 1
    has_output = len(required_output) >= 1
    has_optional_options = len(intro.optional_input) + len(
        intro.optional_output) >= 1

    # Add a C++ style comment block with some additional markings (@param,
    # @return)
    if declaration_only:
        result = f'\n/**\n * {intro.description.capitalize()}.'

        for name in intro.method_args:
            result += f"\n * @param {cppize(name)} {intro.details[name]['blurb']}."

        if has_output:
            # skip the first element
            for name in required_output[1:]:
                result += f"\n * @param {cppize(name)} {intro.details[name]['blurb']}."

        if has_optional_options:
            result += '\n * @param js_options Optional options.'

        if has_output:
            result += f"\n * @return {intro.details[required_output[0]]['blurb']}."

        result += '\n */\n'
    else:
        result = '\n'

    if intro.member_x is None and declaration_only:
        result += 'static '
    if has_output:
        # the first output arg will be used as the result
        cpp_type = get_cpp_type(intro.details[required_output[0]]['type'],
                                False)
        spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
        result += f'{cpp_type}{spacing}'
    else:
        result += 'void '

    if not declaration_only:
        result += 'Image::'

    cplusplus_operation = operation_name
    if operation_name in cplusplus_keywords:
        cplusplus_operation += '_image'

    result += f'{cplusplus_operation}('
    for i, name in enumerate(intro.method_args):
        details = intro.details[name]
        gtype = details['type']
        cpp_type = get_cpp_type(gtype, True)
        spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
        result += f'{cpp_type}{spacing}{cppize(name)}'
        if i != len(intro.method_args) - 1:
            result += ', '

    # output params are passed by reference
    if has_output:
        # skip the first element
        for i, name in enumerate(required_output[1:]):
            details = intro.details[name]
            gtype = details['type']
            cpp_type = get_cpp_type(gtype, False)
            spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
            result += f'{cpp_type}{spacing}*{cppize(name)}'
            if i != len(required_output) - 2:
                result += ', '

    if has_optional_options:
        if has_input or len(required_output) > 1:
            result += ', '
        result += f"emscripten::val js_options{' = emscripten::val::null()' if declaration_only else ''}"

    result += ')'

    # if no 'this' available, it's a class method and they are all const
    if intro.member_x is not None:
        result += ' const'

    if declaration_only:
        result += ';'

        return result

    result += '\n{\n'

    if has_output:
        # the first output arg will be used as the result
        name = required_output[0]
        gtype = intro.details[name]['type']
        cpp_type = 'VipsBlob *' if (
            gtype == GValue.blob_type) else get_cpp_type(gtype, False)
        spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
        result += f'    {cpp_type}{spacing}{ cppize(name)};\n\n'

    separate_blob = False

    for name in intro.method_args:
        if intro.details[name]['type'] == GValue.blob_type:
            #  We must take a copy of the data.
            result += f'    VipsBlob *blob = vips_blob_copy({name}.c_str(), {name}.size());\n'
            separate_blob = True
            break

    if not separate_blob:
        result += f"    {'Image::' if intro.member_x is None else 'this->'}"
        result += f'call("{operation_name}",'
        result += f"{' nullptr,' if intro.member_x is None else ''}\n"

    padding = ' ' * 16 if intro.member_x is None else ' ' * 15

    if separate_blob:
        padding += ' ' * 6
        result += '    Option *options = (new Option)'
    else:
        result += f'{padding}(new Option)'

    if intro.member_x is not None:
        result += f'\n    {padding}->set("{intro.member_x}", *this)'

    all_required = intro.method_args

    if has_output:
        # first element needs to be passed by reference
        arg = cppize(required_output[0])
        result += f'\n    {padding}->set("{required_output[0]}", &{arg})'

        # append the remaining list
        all_required += required_output[1:]

    for name in all_required:
        gtype = intro.details[name]['type']

        if gtype == GValue.blob_type:
            arg = 'blob'
        elif name not in required_output and get_cpp_type(
                gtype, True) == 'emscripten::val':
            type = to_snake_case(remove_prefix(type_name(gtype))).upper()
            arg = f'VIPS_TYPE_{type}, {cppize(name)}'
            # a match image is needed for image types
            if intro.member_x is not None \
                and (gtype == GValue.image_type or
                     gtype == GValue.array_image_type):
                arg += ', this'
        else:
            arg = cppize(name)

        result += f'\n    {padding}->set("{name}", {arg})'

    if separate_blob:
        result += ';\n'
        result += '    vips_area_unref(VIPS_AREA(blob));\n\n'
        result += f"    {'Image::' if intro.member_x is None else 'this->'}"
        result += f'call("{operation_name}",'
        result += f"{' nullptr,' if intro.member_x is None else ''} options, js_options);\n"
    else:
        result += f',\n{padding}js_options);\n' if has_optional_options else ');\n'

    if has_output:
        gtype = intro.details[required_output[0]]['type']

        result += '\n'
        if gtype == GValue.blob_type:
            result += '    emscripten::val result = emscripten::val(emscripten::typed_memory_view(\n'
            result += f'        VIPS_AREA({required_output[0]})->length,\n'
            result += f'        reinterpret_cast<uint8_t *>(VIPS_AREA({required_output[0]})->data)));\n'
            result += f'    VIPS_AREA({required_output[0]})->free_fn = nullptr;\n'
            result += f'    vips_area_unref(VIPS_AREA({required_output[0]}));\n\n'
            result += '    return result;\n'
        else:
            result += f'    return {required_output[0]};\n'

    result += '}'

    return result
Ejemplo n.º 14
0
    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)))