Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- add test coverage for type stubs [JoshCLWren]
- add `Image.pil()` to convert to a PIL image [jonashaag]
- add `pyvips.concurrency_set()`, `pyvips.concurrency_get()` [NewUserHa]
- add support for marshaling pointers [kleisauke]

## Version 3.1.1 (released 9 December 2025)

Expand Down
20 changes: 20 additions & 0 deletions examples/draw_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env python3

# import logging
# logging.basicConfig(level = logging.DEBUG)

import pyvips
from pyvips import ffi


def custom_draw(image, ink, x, y, client):
assert ffi.from_handle(client) == 42
print(x, y)


cb = ffi.callback('VipsDrawPoint', custom_draw)
client = ffi.new_handle(42)

im = pyvips.Image.black(100, 100)
im = im.draw_line([100], 0, 0, 100, 0,
draw_point=cb, client=client) # type: ignore[call-arg]
6 changes: 6 additions & 0 deletions pyvips/gvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class GValue(object):
guint64_type = type_from_name('guint64')
gdouble_type = type_from_name('gdouble')
gstr_type = type_from_name('gchararray')
gpointer_type = type_from_name('gpointer')
genum_type = type_from_name('GEnum')
gflags_type = type_from_name('GFlags')
gobject_type = type_from_name('GObject')
Expand All @@ -57,6 +58,7 @@ class GValue(object):
guint64_type: 'int',
gdouble_type: 'float',
gstr_type: 'str',
gpointer_type: 'ffi.CData',
refstr_type: 'str',
genum_type: 'str',
gflags_type: 'int',
Expand Down Expand Up @@ -188,6 +190,8 @@ def set(self, value):
GValue.to_flag(gtype, value))
elif gtype == GValue.gstr_type:
gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value))
elif gtype == GValue.gpointer_type:
gobject_lib.g_value_set_pointer(self.gvalue, value)
elif gtype == GValue.refstr_type:
vips_lib.vips_value_set_ref_string(self.gvalue, _to_bytes(value))
elif fundamental == GValue.gobject_type:
Expand Down Expand Up @@ -266,6 +270,8 @@ def get(self):

if pointer != ffi.NULL:
result = _to_string(pointer)
elif gtype == GValue.gpointer_type:
result = gobject_lib.g_value_get_pointer(self.gvalue)
elif gtype == GValue.refstr_type:
psize = ffi.new('size_t *')
pointer = vips_lib.vips_value_get_ref_string(self.gvalue, psize)
Expand Down
1 change: 1 addition & 0 deletions pyvips/gvalue.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GValue(object):
guint64_type: ClassVar[int]
gdouble_type: ClassVar[int]
gstr_type: ClassVar[int]
gpointer_type: ClassVar[int]
genum_type: ClassVar[int]
gflags_type: ClassVar[int]
gobject_type: ClassVar[int]
Expand Down
49 changes: 29 additions & 20 deletions pyvips/vdecls.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def cdefs(features):
GType gtype, const char* str);
int vips_flags_from_nick (const char* domain,
GType gtype, const char* nick);
const char *vips_enum_nick (GType gtype, int value);
const char* vips_enum_nick (GType gtype, int value);

void g_value_set_boolean (GValue* value, int v_boolean);
void g_value_set_int (GValue* value, int i);
Expand All @@ -108,13 +108,14 @@ def cdefs(features):
void g_value_set_enum (GValue* value, int e);
void g_value_set_flags (GValue* value, unsigned int f);
void g_value_set_string (GValue* value, const char* str);
void g_value_set_pointer (GValue* value, void* v_pointer);
void vips_value_set_ref_string (GValue* value, const char* str);
void g_value_set_object (GValue* value, void* object);
void vips_value_set_array_double (GValue* value,
const double* array, int n );
void vips_value_set_array_int (GValue* value,
const int* array, int n );
void vips_value_set_array_image (GValue *value, int n);
void vips_value_set_array_image (GValue* value, int n);

int g_value_get_boolean (const GValue* value);
int g_value_get_int (GValue* value);
Expand All @@ -123,6 +124,7 @@ def cdefs(features):
int g_value_get_enum (GValue* value);
unsigned int g_value_get_flags (GValue* value);
const char* g_value_get_string (GValue* value);
void* g_value_get_pointer (GValue* value);
const char* vips_value_get_ref_string (const GValue* value,
size_t* length);
void* g_value_get_object (GValue* value);
Expand All @@ -143,14 +145,14 @@ def cdefs(features):
} GTypeClass;

typedef struct _GTypeInstance {
GTypeClass *g_class;
GTypeClass* g_class;
} GTypeInstance;

typedef struct _GObject {
GTypeInstance g_type_instance;

unsigned int ref_count;
GData *qdata;
GData* qdata;
} GObject;

typedef struct _GParamSpec {
Expand All @@ -173,8 +175,8 @@ def cdefs(features):
typedef struct _GEnumValue {
int value;

const char *value_name;
const char *value_nick;
const char* value_name;
const char* value_nick;
} GEnumValue;

typedef struct _GEnumClass {
Expand All @@ -183,22 +185,22 @@ def cdefs(features):
int minimum;
int maximum;
unsigned int n_values;
GEnumValue *values;
GEnumValue* values;
} GEnumClass;

typedef struct _GFlagsValue {
unsigned int value;

const char *value_name;
const char *value_nick;
const char* value_name;
const char* value_nick;
} GFlagsValue;

typedef struct _GFlagsClass {
GTypeClass g_type_class;

unsigned int mask;
unsigned int n_values;
GFlagsValue *values;
GFlagsValue* values;
} GFlagsClass;

void* g_type_class_ref (GType type);
Expand All @@ -208,14 +210,14 @@ def cdefs(features):
void g_object_unref (void* object);

void g_object_set_property (GObject* object,
const char *name, GValue* value);
const char* name, GValue* value);
void g_object_get_property (GObject* object,
const char* name, GValue* value);

void vips_image_invalidate_all (VipsImage* image);

typedef void (*GCallback)(void);
typedef void (*GClosureNotify)(void* data, struct _GClosure *);
typedef void (*GCallback) (void);
typedef void (*GClosureNotify) (void* data, struct _GClosure* );
long g_signal_connect_data (GObject* object,
const char* detailed_signal,
GCallback c_handler,
Expand Down Expand Up @@ -247,7 +249,7 @@ def cdefs(features):
typedef ... VipsObjectClass;

typedef struct _VipsArgument {
GParamSpec *pspec;
GParamSpec* pspec;
} VipsArgument;

typedef struct _VipsArgumentInstance {
Expand All @@ -271,14 +273,14 @@ def cdefs(features):
typedef struct _VipsArgumentClass {
VipsArgument parent;

VipsObjectClass *object_class;
VipsObjectClass* object_class;
VipsArgumentFlags flags;
int priority;
unsigned int offset;
} VipsArgumentClass;

int vips_object_get_argument (VipsObject* object,
const char *name, GParamSpec** pspec,
const char* name, GParamSpec** pspec,
VipsArgumentClass** argument_class,
VipsArgumentInstance** argument_instance);

Expand Down Expand Up @@ -365,7 +367,7 @@ def cdefs(features):
# vips_value_set_blob_free in a backwards compatible way
if not features['api']:
code += '''
typedef void (*FreeFn)(void* a);
typedef void (*FreeFn) (void* a);
void vips_value_set_blob (GValue* value,
FreeFn free_fn, void* data, size_t length);
'''
Expand Down Expand Up @@ -443,7 +445,7 @@ def cdefs(features):
extern "Python" void _marshal_finish (VipsTarget*,
void*);

const char* vips_foreign_find_load_source (VipsSource *source);
const char* vips_foreign_find_load_source (VipsSource* source);
const char* vips_foreign_find_save_target (const char* suffix);

'''
Expand All @@ -454,16 +456,23 @@ def cdefs(features):
void*);

void vips_block_untrusted_set (int state);
void vips_operation_block_set (const char *name, int state);
void vips_operation_block_set (const char* name, int state);

'''

if _at_least(features, 8, 18):
code += '''
VipsImage *vips_image_get_gainmap(VipsImage *image);
VipsImage* vips_image_get_gainmap(VipsImage* image);

'''

if _at_least(features, 8, 19):
code += '''
typedef unsigned char VipsPel;
typedef void (*VipsDrawPoint) (VipsImage* image, VipsPel* ink,
int x, int y, void* client);
'''

# we must only define these in API mode ... in ABI mode we need to call
# these things earlier
if features['api']:
Expand Down
8 changes: 8 additions & 0 deletions tests/test_gvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ def test_string(self):
value = gv.get()
assert value == 'banana'

def test_pointer(self):
pointer = pyvips.ffi.new_handle(42)
gv = pyvips.GValue()
gv.set_type(pyvips.GValue.gpointer_type)
gv.set(pointer)
value = gv.get()
assert pyvips.ffi.from_handle(value) == 42

def test_array_int(self):
gv = pyvips.GValue()
gv.set_type(pyvips.GValue.array_int_type)
Expand Down