Skip to content

Commit c90b657

Browse files
authored
Improve support for GFlags types (#429)
* Support passing strings to `GFlags` types For example: ``` im.pngsave('x.png', filter='all') ``` * Regenerate enums with libvips 8.14 * Support auto-generation of `GFlags` types * Fix flake8
1 parent 4e60225 commit c90b657

File tree

5 files changed

+171
-49
lines changed

5 files changed

+171
-49
lines changed

examples/gen-enums.py

+30-16
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import sys
44
import xml.etree.ElementTree as ET
55

6-
from pyvips import ffi, values_for_enum, vips_lib, \
7-
type_map, type_name, type_from_name
6+
from pyvips import ffi, values_for_enum, values_for_flag, \
7+
vips_lib, type_map, type_name, type_from_name
88

99
# This file generates enums.py -- the set of classes giving the permissible
10-
# values for the pyvips enums. Run with something like:
10+
# values for the pyvips enums/flags. Run with something like:
1111
#
1212
# ./gen-enums.py ~/GIT/libvips/libvips/Vips-8.0.gir > enums.py
1313
# mv enums.py ../pyvips
@@ -18,11 +18,15 @@
1818
"goi": "http://www.gtk.org/introspection/core/1.0"
1919
}
2020

21-
# find all the enumerations and make a dict for them
21+
# find all the enumerations/flags and make a dict for them
2222
xml_enums = {}
2323
for node in root.findall("goi:namespace/goi:enumeration", namespace):
2424
xml_enums[node.get('name')] = node
2525

26+
xml_flags = {}
27+
for node in root.findall("goi:namespace/goi:bitfield", namespace):
28+
xml_flags[node.get('name')] = node
29+
2630

2731
def remove_prefix(enum_str):
2832
prefix = 'Vips'
@@ -33,31 +37,41 @@ def remove_prefix(enum_str):
3337
return enum_str
3438

3539

36-
def generate_enums():
40+
def generate_enums_flags():
3741
# otherwise we're missing some enums
3842
vips_lib.vips_token_get_type()
3943
vips_lib.vips_saveable_get_type()
4044
vips_lib.vips_image_type_get_type()
4145

42-
all_enums = []
46+
all_nicknames = []
4347

44-
def add_enum(gtype, a, b):
48+
def add_nickname(gtype, a, b):
4549
nickname = type_name(gtype)
46-
all_enums.append(nickname)
50+
all_nicknames.append(nickname)
4751

48-
type_map(gtype, add_enum)
52+
type_map(gtype, add_nickname)
4953

5054
return ffi.NULL
5155

52-
type_map(type_from_name('GEnum'), add_enum)
56+
type_map(type_from_name('GEnum'), add_nickname)
57+
type_map(type_from_name('GFlags'), add_nickname)
58+
59+
# Filter internal flags
60+
filter = ['VipsForeignFlags']
61+
all_nicknames = [name for name in all_nicknames if name not in filter]
5362

54-
for name in all_enums:
63+
for name in all_nicknames:
5564
gtype = type_from_name(name)
5665
python_name = remove_prefix(name)
57-
if python_name not in xml_enums:
66+
if python_name in xml_enums:
67+
node = xml_enums[python_name]
68+
values = values_for_enum(gtype)
69+
elif python_name in xml_flags:
70+
node = xml_flags[python_name]
71+
values = values_for_flag(gtype)
72+
else:
5873
continue
5974

60-
node = xml_enums[python_name]
6175
enum_doc = node.find("goi:doc", namespace)
6276

6377
print('')
@@ -70,7 +84,7 @@ def add_enum(gtype, a, b):
7084
print('')
7185
print('Attributes:')
7286
print('')
73-
for value in values_for_enum(gtype):
87+
for value in values:
7488
python_name = value.replace('-', '_')
7589
member = node.find(f"goi:member[@name='{python_name}']", namespace)
7690
member_doc = member.find("goi:doc", namespace)
@@ -81,12 +95,12 @@ def add_enum(gtype, a, b):
8195
print(' """')
8296
print('')
8397

84-
for value in values_for_enum(gtype):
98+
for value in values:
8599
python_name = value.replace('-', '_').upper()
86100
print(f' {python_name} = \'{value}\'')
87101

88102

89103
if __name__ == "__main__":
90104
print('# libvips enums -- this file is generated automatically')
91105
print('# flake8: noqa: E501') # ignore line too long error
92-
generate_enums()
106+
generate_enums_flags()

pyvips/enums.py

+118-30
Original file line numberDiff line numberDiff line change
@@ -250,30 +250,28 @@ class Interpretation(object):
250250
class DemandStyle(object):
251251
"""DemandStyle.
252252
253-
See vips_image_pipelinev(). Operations can hint to the VIPS image IO
254-
system about the kind of demand geometry they prefer.
253+
See vips_image_pipelinev(). Operations can hint
254+
the kind of demand geometry they prefer
255+
to the VIPS image IO system.
255256
256257
These demand styles are given below in order of increasing
257-
restrictiveness. When demanding output from a pipeline,
258+
specialisation. When demanding output from a pipeline,
258259
vips_image_generate()
259-
will use the most restrictive of the styles requested by the operations
260+
will use the most general style requested by the operations
260261
in the pipeline.
261262
262-
#VIPS_DEMAND_STYLE_THINSTRIP --- This operation would like to output strips
263-
the width of the image and a few pels high. This is option suitable for
264-
point-to-point operations, such as those in the arithmetic package.
265-
266-
This option is only efficient for cases where each output pel depends
267-
upon the pel in the corresponding position in the input image.
263+
#VIPS_DEMAND_STYLE_SMALLTILE --- This is the most general demand format.
264+
Output is demanded in small (around 100x100 pel) sections. This style works
265+
reasonably efficiently, even for bizzarre operations like 45 degree rotate.
268266
269267
#VIPS_DEMAND_STYLE_FATSTRIP --- This operation would like to output strips
270268
the width of the image and as high as possible. This option is suitable
271269
for area operations which do not violently transform coordinates, such
272270
as vips_conv().
273271
274-
#VIPS_DEMAND_STYLE_SMALLTILE --- This is the most general demand format.
275-
Output is demanded in small (around 100x100 pel) sections. This style works
276-
reasonably efficiently, even for bizzare operations like 45 degree rotate.
272+
#VIPS_DEMAND_STYLE_THINSTRIP --- This operation would like to output strips
273+
the width of the image and a few pels high. This option is suitable for
274+
point-to-point operations, such as those in the arithmetic package.
277275
278276
#VIPS_DEMAND_STYLE_ANY --- This image is not being demand-read from a disc
279277
file (even indirectly) so any demand style is OK. It's used for things like
@@ -283,9 +281,9 @@ class DemandStyle(object):
283281
284282
Attributes:
285283
286-
SMALLTILE (str): demand in small (typically 64x64 pixel) tiles
284+
SMALLTILE (str): demand in small (typically 128x128 pixel) tiles
287285
288-
FATSTRIP (str): demand in fat (typically 10 pixel high) strips
286+
FATSTRIP (str): demand in fat (typically 16 pixel high) strips
289287
290288
THINSTRIP (str): demand in thin (typically 1 pixel high) strips
291289
@@ -359,11 +357,11 @@ class OperationMath2(object):
359357
360358
Attributes:
361359
362-
POW (str): pow( left, right )
360+
POW (str): pow(left, right)
363361
364-
WOP (str): pow( right, left )
362+
WOP (str): pow(right, left)
365363
366-
ATAN2 (str): atan2( left, right )
364+
ATAN2 (str): atan2(left, right)
367365
368366
"""
369367

@@ -405,6 +403,14 @@ class OperationMath(object):
405403
406404
ATAN (str): atan(), angles in degrees
407405
406+
LOG (str): log base e
407+
408+
LOG10 (str): log base 10
409+
410+
EXP (str): e to the something
411+
412+
EXP10 (str): 10 to the something
413+
408414
SINH (str): sinh(), angles in radians
409415
410416
COSH (str): cosh(), angles in radians
@@ -417,14 +423,6 @@ class OperationMath(object):
417423
418424
ATANH (str): atanh(), angles in radians
419425
420-
LOG (str): log base e
421-
422-
LOG10 (str): log base 10
423-
424-
EXP (str): e to the something
425-
426-
EXP10 (str): 10 to the something
427-
428426
"""
429427

430428
SIN = 'sin'
@@ -433,16 +431,16 @@ class OperationMath(object):
433431
ASIN = 'asin'
434432
ACOS = 'acos'
435433
ATAN = 'atan'
434+
LOG = 'log'
435+
LOG10 = 'log10'
436+
EXP = 'exp'
437+
EXP10 = 'exp10'
436438
SINH = 'sinh'
437439
COSH = 'cosh'
438440
TANH = 'tanh'
439441
ASINH = 'asinh'
440442
ACOSH = 'acosh'
441443
ATANH = 'atanh'
442-
LOG = 'log'
443-
LOG10 = 'log10'
444-
EXP = 'exp'
445-
EXP10 = 'exp10'
446444

447445

448446
class OperationRound(object):
@@ -808,6 +806,32 @@ class Precision(object):
808806
APPROXIMATE = 'approximate'
809807

810808

809+
class TextWrap(object):
810+
"""TextWrap.
811+
812+
Sets the word wrapping style for vips_text() when used with a maximum
813+
width.
814+
815+
See also: vips_text().
816+
817+
Attributes:
818+
819+
WORD (str): wrap at word boundaries
820+
821+
CHAR (str): wrap at character boundaries
822+
823+
WORD_CHAR (str): wrap at word boundaries, but fall back to character boundaries if there is not enough space for a full word
824+
825+
NONE (str): no wrapping
826+
827+
"""
828+
829+
WORD = 'word'
830+
CHAR = 'char'
831+
WORD_CHAR = 'word-char'
832+
NONE = 'none'
833+
834+
811835
class FailOn(object):
812836
"""FailOn.
813837
@@ -848,6 +872,9 @@ class ForeignPpmFormat(object):
848872
849873
#VIPS_FOREIGN_PPM_FORMAT_PFM images are 32-bit float pixels.
850874
875+
#VIPS_FOREIGN_PPM_FORMAT_PNM images are anymap images -- the image format
876+
is used to pick the saver.
877+
851878
Attributes:
852879
853880
PBM (str): portable bitmap
@@ -858,12 +885,15 @@ class ForeignPpmFormat(object):
858885
859886
PFM (str): portable float map
860887
888+
PNM (str): portable anymap
889+
861890
"""
862891

863892
PBM = 'pbm'
864893
PGM = 'pgm'
865894
PPM = 'ppm'
866895
PFM = 'pfm'
896+
PNM = 'pnm'
867897

868898

869899
class ForeignSubsample(object):
@@ -1119,6 +1149,34 @@ class ForeignHeifCompression(object):
11191149
AV1 = 'av1'
11201150

11211151

1152+
class ForeignHeifEncoder(object):
1153+
"""ForeignHeifEncoder.
1154+
1155+
The selected encoder to use.
1156+
If libheif hasn't been compiled with the selected encoder,
1157+
we will fallback to the default encoder for the compression format.
1158+
1159+
Attributes:
1160+
1161+
AUTO (str): auto
1162+
1163+
AOM (str): aom
1164+
1165+
RAV1E (str): RAV1E
1166+
1167+
SVT (str): SVT-AV1
1168+
1169+
X265 (str): x265
1170+
1171+
"""
1172+
1173+
AUTO = 'auto'
1174+
AOM = 'aom'
1175+
RAV1E = 'rav1e'
1176+
SVT = 'svt'
1177+
X265 = 'x265'
1178+
1179+
11221180
class Size(object):
11231181
"""Size.
11241182
@@ -1316,3 +1374,33 @@ class ImageType(object):
13161374
MMAPIN = 'mmapin'
13171375
MMAPINRW = 'mmapinrw'
13181376
OPENOUT = 'openout'
1377+
1378+
1379+
class ForeignPngFilter(object):
1380+
"""ForeignPngFilter.
1381+
1382+
http://www.w3.org/TR/PNG-Filters.html
1383+
The values mirror those of png.h in libpng.
1384+
1385+
Attributes:
1386+
1387+
NONE (str): no filtering
1388+
1389+
SUB (str): difference to the left
1390+
1391+
UP (str): difference up
1392+
1393+
AVG (str): average of left and up
1394+
1395+
PAETH (str): pick best neighbor predictor automatically
1396+
1397+
ALL (str): adaptive
1398+
1399+
"""
1400+
1401+
NONE = 'none'
1402+
SUB = 'sub'
1403+
UP = 'up'
1404+
AVG = 'avg'
1405+
PAETH = 'paeth'
1406+
ALL = 'all'

pyvips/gvalue.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,23 @@ def from_enum(gtype, enum_value):
126126

127127
return _to_string(pointer)
128128

129+
@staticmethod
130+
def to_flag(gtype, value):
131+
"""Turn a string into a flag value ready to be passed into libvips.
132+
133+
"""
134+
135+
if isinstance(value, basestring if _is_PY2 else str): # noqa: F821
136+
flag_value = vips_lib.vips_flags_from_nick(b'pyvips', gtype,
137+
_to_bytes(value))
138+
if flag_value < 0:
139+
raise Error('no value {0} in gtype {1} ({2})'.
140+
format(value, type_name(gtype), gtype))
141+
else:
142+
flag_value = value
143+
144+
return flag_value
145+
129146
def __init__(self):
130147
# allocate memory for the gvalue which will be freed on GC
131148
self.pointer = ffi.new('GValue *')
@@ -173,7 +190,8 @@ def set(self, value):
173190
gobject_lib.g_value_set_enum(self.gvalue,
174191
GValue.to_enum(gtype, value))
175192
elif fundamental == GValue.gflags_type:
176-
gobject_lib.g_value_set_flags(self.gvalue, value)
193+
gobject_lib.g_value_set_flags(self.gvalue,
194+
GValue.to_flag(gtype, value))
177195
elif gtype == GValue.gstr_type:
178196
gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value))
179197
elif gtype == GValue.refstr_type:

pyvips/vdecls.py

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def cdefs(features):
9292
9393
int vips_enum_from_nick (const char* domain,
9494
GType gtype, const char* str);
95+
int vips_flags_from_nick (const char* domain,
96+
GType gtype, const char* nick);
9597
const char *vips_enum_nick (GType gtype, int value);
9698
9799
void g_value_set_boolean (GValue* value, int v_boolean);

0 commit comments

Comments
 (0)