1/* A list of things. Do automatic iteration of unary and binary operators on
2 * us.
3 *	List [1, 2] + [2, 3] -> List [3, 5]
4 *	hd (List [2, 3]) -> 2
5 *	List [] == [] -> true
6 */
7List value = class
8	_Object {
9	_check_args = [
10		[value, "value", check_list]
11	];
12
13	// methods
14    oo_binary_table op x = [
15		[apply2 op value x',
16			op.op_name == "subscript" || op.op_name == "subscript'" ||
17			op.op_name == "equal" || op.op_name == "equal'"],
18		[this.List (apply2 op value x'),
19			op.op_name == "join" || op.op_name == "join'"],
20		[this.List (map2 (apply2 op) value x'),
21			is_list x'],
22		[this.List (map (apply2 op' x) value),
23			true]
24	] ++ super.oo_binary_table op x
25	{
26		op' = oo_converse op;
27
28		// strip the List wrapper, if any
29		x'
30			= x.value, is_List x
31			= x;
32
33		apply2 op x1 x2
34			= oo_binary_function op x1 x2, is_class x1
35			= oo_binary'_function op x1 x2, is_class x2
36			= op.fn x1 x2;
37	};
38
39	oo_unary_table op = [
40		[apply value,
41			op.op_name == "hd" || op.op_name == "tl"],
42		[this.List (map apply value),
43			true]
44	] ++ super.oo_unary_table op
45	{
46		apply x
47			= oo_unary_function op x, is_class x
48			= op.fn x;
49	}
50}
51
52/* A group of things. Loop the operation over the group.
53 */
54Group value = class
55	_Object {
56	_check_args = [
57		[value, "value", check_list]
58	];
59
60	// methods
61	oo_binary_table op x = [
62		// if_then_else is really a trinary operator
63		[map_trinary ite this x?0 x?1,
64			op.op_name == "if_then_else"],
65		[map_binary op.fn this x,
66			is_Group x],
67		[map_unary (\a op.fn a x) this,
68			true]
69	] ++ super.oo_binary_table op x;
70
71	oo_unary_table op = [
72		[map_unary op.fn this,
73			true]
74	] ++ super.oo_unary_table op;
75
76	// we can't call map_trinary directly, since it uses Group and we
77	// don't support mutually recursive top-level functions :-(
78	// copy-paste it here, keep in sync with the version in _stdenv
79	map_nary fn args
80		= fn args, groups == []
81		= Group (map process [0, 1 .. shortest - 1])
82	{
83		groups = filter is_Group args;
84
85		shortest = foldr1 min_pair (map (len @ get_value) groups);
86
87		process n
88			= NULL, any (map (is_noval n) args)
89			= map_nary fn (map (extract n) args)
90		{
91			extract n arg
92				= arg.value?n, is_Group arg
93				= arg;
94
95			is_noval n arg = is_Group arg && arg.value?n == NULL;
96		}
97	}
98
99	// need ite as a true trinary
100	ite a b c = if a then b else c;
101
102	map_unary fn a = map_nary (list_1ary fn) [a];
103	map_binary fn a b = map_nary (list_2ary fn) [a, b];
104	map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c];
105}
106
107/* Single real number ... eg slider.
108 */
109Real value = class
110	_Object {
111	_check_args = [
112		[value, "value", check_real]
113	];
114
115	// methods
116	oo_binary_table op x = [
117		[this.Real (op.fn this.value x.value),
118			is_Real x &&
119			op.type == Operator_type.ARITHMETIC],
120		[this.Real (op.fn this.value x),
121			is_real x &&
122			op.type == Operator_type.ARITHMETIC],
123		[op.fn this.value x.value,
124			is_Real x &&
125			op.type == Operator_type.RELATIONAL],
126		[op.fn this.value x,
127			!is_class x]
128	] ++ super.oo_binary_table op x;
129
130	oo_unary_table op = [
131		[this.Real (op.fn this.value),
132			op.type == Operator_type.ARITHMETIC],
133		[op.fn this.value,
134			true]
135	] ++ super.oo_unary_table op;
136}
137
138/* Single bool ... eg Toggle.
139 */
140Bool value = class
141	_Object {
142	_check_args = [
143		[value, "value", check_bool]
144	];
145
146	// methods
147	oo_binary_table op x = [
148		[op.fn this.value x,
149			op.op_name == "if_then_else"],
150		[this.Bool (op.fn this.value x.value),
151			is_Bool x],
152		[this.Bool (op.fn this.value x),
153			is_bool x]
154	] ++ super.oo_binary_table op x;
155
156	oo_unary_table op = [
157		[this.Bool (op.fn this.value),
158			op.type == Operator_type.ARITHMETIC ||
159			op.type == Operator_type.RELATIONAL],
160		[op.fn this.value,
161			true]
162	] ++ super.oo_unary_table op;
163}
164
165/* An editable string.
166 */
167String caption value = class
168	_Object {
169	_check_args = [
170		[caption, "caption", check_string],
171		[value, "value", check_string]
172	];
173}
174
175/* An editable real number.
176 */
177Number caption value = class
178	scope.Real value {
179	_check_args = [
180		[caption, "caption", check_string]
181	];
182
183	Real x = this.Number caption x;
184}
185
186/* An editable expression.
187 */
188Expression caption expr = class
189	(if is_class expr then expr else _Object) {
190	_check_args = [
191		[caption, "caption", check_string],
192		[expr, "expr", check_any]
193	];
194}
195
196/* A ticking clock.
197 */
198Clock interval value = class
199	scope.Real value {
200	_check_args = [
201		[interval, "interval", check_real]
202	];
203
204	Real x = this.Clock interval x;
205}
206
207/* An editable filename.
208 */
209Pathname caption value = class
210	_Object {
211	_check_args = [
212		[caption, "caption", check_string],
213		[value, "value", check_string]
214	];
215}
216
217/* An editable fontname.
218 */
219Fontname caption value = class
220	_Object {
221	_check_args = [
222		[caption, "caption", check_string],
223		[value, "value", check_string]
224	];
225}
226
227/* Vector type ... just a finite list of real. Handy for wrapping an
228 * argument to eg. im_lintra_vec. Make it behave like a single pixel image.
229 */
230Vector value = class
231	_Object {
232	_check_args = [
233		[value, "value", check_real_list]
234	];
235
236	bands = len value;
237
238	// methods
239	oo_binary_table op x = [
240		// Vector ++ Vector means bandwise join
241		[this.Vector (op.fn this.value x.value),
242			is_Vector x &&
243			(op.op_name == "join" || op.op_name == "join'")],
244		[this.Vector (op.fn this.value [get_number x]),
245			has_number x &&
246			(op.op_name == "join" || op.op_name == "join'")],
247		// Vector ? number means extract element
248		[op.fn this.value (get_real x),
249			has_real x &&
250			(op.op_name == "subscript" ||
251				op.op_name == "subscript'")],
252		// extra check for lengths equal
253		[this.Vector (map_binaryl op.fn this.value x.value),
254			is_Vector x &&
255			len value == len x.value &&
256			op.type == Operator_type.ARITHMETIC],
257		[this.Vector (map_binaryl op.fn this.value (get_real x)),
258			has_real x &&
259			op.type == Operator_type.ARITHMETIC],
260
261		// need extra length check
262		[this.Vector (map bool_to_real
263			(map_binaryl op.fn this.value x.value)),
264			is_Vector x &&
265			len value == len x.value &&
266			op.type == Operator_type.RELATIONAL],
267		[this.Vector (map bool_to_real
268			(map_binaryl op.fn this.value (get_real x))),
269			has_real x &&
270			op.type == Operator_type.RELATIONAL],
271		[this.Vector (op.fn this.value x.value),
272			is_Vector x &&
273			len value == len x.value &&
274			op.type == Operator_type.COMPOUND_REWRAP],
275		[x.Image (vec op'.op_name x.value value),
276			is_Image x],
277		[vec op'.op_name x value,
278			is_image x],
279		[op.fn this.value x,
280			is_real x]
281	] ++ super.oo_binary_table op x
282	{
283		op' = oo_converse op;
284	};
285
286	oo_unary_table op = [
287		[this.Vector (map_unaryl op.fn this.value),
288			op.type == Operator_type.ARITHMETIC],
289		[this.Vector (map bool_to_real
290			(map_unaryl op.fn this.value)),
291			op.type == Operator_type.RELATIONAL],
292		[this.Vector (op.fn this.value),
293			op.type == Operator_type.COMPOUND_REWRAP],
294		[op.fn this.value,
295			true]
296	] ++ super.oo_unary_table op;
297
298	// turn an ip bool (or a number, for Vector) into VIPSs 255/0
299	bool_to_real x
300		= 255, is_bool x && x
301		= 255, is_number x && x != 0
302		= 0;
303}
304
305/* A rectangular array of real.
306 */
307Matrix_base value = class
308	_Object {
309	_check_args = [
310		[value, "value", check_matrix]
311	];
312
313	// calculate these from value
314	width = len value?0;
315	height = len value;
316
317	// extract a rectanguar area
318	extract left top width height
319		= this.Matrix_base
320			((map (take width) @ map (drop left) @
321				take height @ drop top) value);
322
323	// methods
324	oo_binary_table op x = [
325		// mat multiply is special
326		[this.Matrix_base mul.value,
327			is_Matrix x &&
328			op.op_name == "multiply"],
329		[this.Matrix_base mul'.value,
330			is_Matrix x &&
331			op.op_name == "multiply'"],
332
333		// mat divide is also special
334		[this.Matrix_base div.value,
335			is_Matrix x &&
336			op.op_name == "divide"],
337		[this.Matrix_base div'.value,
338			is_Matrix x &&
339			op.op_name == "divide'"],
340
341		// power -1 means invert
342		[this.Matrix_base inv.value,
343			is_real x && x == -1 &&
344			op.op_name == "power"],
345		[this.Matrix_base sq.value,
346			is_real x && x == 2 &&
347			op.op_name == "power"],
348		[error "matrix **-1 and **2 only",
349			op.op_name == "power" ||
350			op.op_name == "power'"],
351
352		// matrix op vector ... treat a vector as a 1 row matrix
353		[this.Matrix_base (map (map_binaryl op'.fn x.value) this.value),
354			is_Vector x &&
355			op.type == Operator_type.ARITHMETIC],
356		[this.Matrix_base (map_binaryl op.fn this.value x.value),
357			(is_Matrix x || is_Real x) &&
358			op.type == Operator_type.ARITHMETIC],
359
360		[this.Matrix_base (map_binaryl op.fn this.value x),
361			is_real x &&
362			op.type == Operator_type.ARITHMETIC],
363
364		// compound ... don't do iteration
365		[this.Matrix_base (op.fn this.value x.value),
366			(is_Matrix x || is_Real x || is_Vector x) &&
367			op.type == Operator_type.COMPOUND_REWRAP],
368
369		[op.fn this.value x,
370			op.type == Operator_type.COMPOUND]
371
372	] ++ super.oo_binary_table op x
373	{
374		mul = im_matmul this x;
375		mul' = im_matmul x this;
376		div = im_matmul this (im_matinv x);
377		div' = im_matmul x (im_matinv this);
378		inv = im_matinv this;
379		sq = im_matmul this this;
380		op' = oo_converse op;
381	}
382
383	oo_unary_table op = [
384		[this.Matrix_base (map_unaryl op.fn this.value),
385			op.type == Operator_type.ARITHMETIC],
386		[this.Matrix_base (op.fn this.value),
387			op.type == Operator_type.COMPOUND_REWRAP],
388		[op.fn this.value,
389			true]
390	] ++ super.oo_unary_table op;
391}
392
393/* How to display a matrix: text, sliders, toggles, or text plus scale/offset.
394 */
395Matrix_display = class {
396        text = 0;
397        slider = 1;
398        toggle = 2;
399        text_scale_offset = 3;
400
401        is_display = member [text, slider, toggle, text_scale_offset];
402}
403
404/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add
405 * a display type as well to control how the widget renders.
406 */
407Matrix_vips value scale offset filename display = class
408	scope.Matrix_base value {
409	_check_args = [
410		[scale, "scale", check_real],
411		[offset, "offset", check_real],
412		[filename, "filename", check_string],
413		[display, "display", check_matrix_display]
414	];
415
416	Matrix_base x = this.Matrix_vips x scale offset filename display;
417}
418
419/* A plain 'ol matrix which can be passed to VIPS.
420 */
421Matrix value = class
422	Matrix_vips value 1 0 "" Matrix_display.text {}
423
424/* Specialised constructors ... for convolutions, recombinations and
425 * morphologies.
426 */
427Matrix_con scale offset value = class
428	Matrix_vips value scale offset "" Matrix_display.text_scale_offset {};
429
430Matrix_rec value = class
431	Matrix_vips value 1 0 "" Matrix_display.slider {};
432
433Matrix_mor value = class
434	Matrix_vips value 1 0 "" Matrix_display.toggle {};
435
436Matrix_file filename = (im_read_dmask @ expand @ search) filename;
437
438/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc)
439 */
440Colour colour_space value = class
441	scope.Vector value {
442	_check_args = [
443		[colour_space, "colour_space", check_colour_space]
444	];
445	_check_all = [
446		[is_list_len 3 value, "len value == 3"]
447	];
448
449	Vector x = this.Colour colour_space x;
450
451	// make a colour-ish thing from an image
452	// back to Colour if we have another 3 band image
453	// to a vector if bands > 1
454	// to a number otherwise
455	itoc im
456		= this.Colour nip_type (to_matrix im).value?0,
457			bands == 3
458		= scope.Vector (map mean (bandsplit im)),
459			bands > 1
460		= mean im
461	{
462		type = get_header "Type" im;
463		bands = get_header "Bands" im;
464		nip_type = Image_type.colour_spaces.lookup 1 0 type;
465	}
466
467	// methods
468	oo_binary_table op x = [
469		[itoc (op.fn
470			((float) (to_image this).value)
471			((float) (to_image x).value)),
472			// here REWRAP means go via image
473			op.type == Operator_type.COMPOUND_REWRAP]
474	] ++ super.oo_binary_table op x;
475
476	oo_unary_table op = [
477		[itoc (op.fn ((float) (to_image this).value)),
478			op.type == Operator_type.COMPOUND_REWRAP]
479	] ++ super.oo_unary_table op;
480}
481
482// a subclass with widgets for picking a space and value
483Colour_picker default_colour default_value = class
484	Colour space.item colour.expr {
485	_vislevel = 3;
486
487	space = Option_enum "Colour space" Image_type.colour_spaces default_colour;
488	colour = Expression "Colour value" default_value;
489
490	Colour_edit colour_space value =
491		Colour_picker colour_space value;
492}
493
494/* Base scale type.
495 */
496Scale caption from to value = class
497	scope.Real value {
498	_check_args = [
499		[caption, "caption", check_string],
500		[from, "from", check_real],
501		[to, "to", check_real]
502	];
503	_check_all = [
504		[from < to, "from < to"]
505	];
506
507	Real x = this.Scale caption from to x;
508
509	// methods
510	oo_binary_table op x = [
511		[this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to)
512			(op.fn this.value x.value),
513			is_Scale x &&
514			op.type == Operator_type.ARITHMETIC],
515		[this.Scale caption (op.fn this.from x) (op.fn this.to x)
516			(op.fn this.value x),
517			is_real x &&
518			op.type == Operator_type.ARITHMETIC]
519	] ++ super.oo_binary_table op x;
520}
521
522/* Base toggle type.
523 */
524Toggle caption value = class
525	scope.Bool value {
526	_check_args = [
527		[caption, "caption", check_string],
528		[value, "value", check_bool]
529	];
530
531	Bool x = this.Toggle caption x;
532}
533
534/* Base option type.
535 */
536Option caption labels value = class
537	scope.Real value {
538	_check_args = [
539		[caption, "caption", check_string],
540		[labels, "labels", check_string_list],
541		[value, "value", check_uint]
542	];
543}
544
545/* An option whose value is a string rather than a number.
546 */
547Option_string caption labels item = class
548	Option caption labels (index (equal item) labels) {
549	Option_edit caption labels value
550		= this.Option_string caption labels (labels?value);
551}
552
553/* Make an option from an enum.
554 */
555Option_enum caption enum item = class
556	Option_string caption enum.names item {
557	// corresponding thing
558	value_thing = enum.get_thing item;
559
560	Option_edit caption labels value
561		= this.Option_enum caption enum (enum.names?value);
562}
563
564/* A rectangle. width and height can be -ve.
565 */
566Rect left top width height = class
567	_Object {
568	_check_args = [
569		[left, "left", check_real],
570		[top, "top", check_real],
571		[width, "width", check_real],
572		[height, "height", check_real]
573	];
574
575	// derived
576	right = left + width;
577	bottom = top + height;
578
579	oo_binary_table op x = [
580		[equal x,
581			is_Rect x &&
582			(op.op_name == "equal" || op.op_name == "equal'")],
583		[!equal x,
584			is_Rect x &&
585			(op.op_name == "not_equal" ||
586				op.op_name == "not_equal'")],
587
588		// binops with a complex are the same as (comp op comp)
589		[oo_binary_function op this (Rect (re x) (im x) 0 0),
590			is_complex x],
591
592		// all others are just pairwise
593		[this.Rect left' top' width' height',
594			is_Rect x &&
595			op.type == Operator_type.ARITHMETIC],
596		[this.Rect left'' top'' width'' height'',
597			has_number x &&
598			op.type == Operator_type.ARITHMETIC]
599	] ++ super.oo_binary_table op x
600	{
601		left' = op.fn left x.left;
602		top' = op.fn top x.top;
603		width' = op.fn width x.width;
604		height' = op.fn height x.height;
605
606		left'' = op.fn left x';
607		top'' = op.fn top x';
608		width'' = op.fn width x';
609		height'' = op.fn height x';
610		x' = get_number x;
611	}
612
613	oo_unary_table op = [
614		// arithmetic uops just map
615		[this.Rect left' top' width' height',
616			op.type == Operator_type.ARITHMETIC],
617
618		// compound uops are just like ops on complex
619		// do (width, height) so thing like abs(Arrow) work as you'd expect
620		[op.fn (width, height),
621			op.type == Operator_type.COMPOUND]
622	] ++ super.oo_unary_table op
623	{
624		left' = op.fn left;
625		top' = op.fn top;
626		width' = op.fn width;
627		height' = op.fn height;
628	}
629
630	// empty? ie. contains no pixels
631	is_empty = width == 0 || height == 0;
632
633	// normalised version, ie. make width/height +ve and flip the origin
634	nleft
635		= left + width, width < 0
636		= left;
637	ntop
638		= top + height, height < 0
639		= top;
640	nwidth = abs width;
641	nheight = abs height;
642	nright = nleft + nwidth;
643	nbottom = ntop + nheight;
644
645	equal x = left == x.left && top == x.top &&
646		  width == x.width && height == x.height;
647
648	// contains a point?
649	includes_point x y
650		= nleft <= x && x <= nright && ntop <= y && y <= nbottom;
651
652	// contains a rect? just test top left and bottom right points
653	includes_rect r
654		= includes_point r.nleft r.ntop &&
655			includes_point r.nright r.nbottom;
656
657	// bounding box of two rects
658	// if either is empty, can just return the other
659	union r
660		= r, is_empty
661		= this, r.is_empty
662		= Rect left' top' width' height'
663	{
664		left' = min_pair nleft r.nleft;
665		top' = min_pair ntop r.ntop;
666		width' = max_pair nright r.nright - left';
667		height' = max_pair nbottom r.nbottom - top';
668	}
669
670	// intersection of two rects ... empty rect if no intersection
671	intersect r
672		= Rect left' top' width'' height''
673	{
674		left' = max_pair nleft r.nleft;
675		top' = max_pair ntop r.ntop;
676		width' = min_pair nright r.nright - left';
677		height' = min_pair nbottom r.nbottom - top';
678		width''
679			= width', width > 0
680			= 0;
681		height''
682			= height', height > 0
683			= 0;
684	}
685
686	// expand/collapse by n pixels
687	margin_adjust n
688		= Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n);
689}
690
691/* Values for Compression field in image.
692 */
693Image_compression = class {
694	NONE = 0;
695	NO_COMPRESSION = 0;
696	TCSF_COMPRESSION = 1;
697	JPEG_COMPRESSION = 2;
698	LABPACK_COMPRESSED = 3;
699	RGB_COMPRESSED = 4;
700	LUM_COMPRESSED = 5;
701}
702
703/* Values for Coding field in image.
704 */
705Image_coding = class {
706	NONE = 0;
707	NOCODING = 0;
708	COLQUANT = 1;
709	LABPACK = 2;
710	RAD = 6;
711}
712
713/* Values for BandFmt field in image.
714 */
715Image_format = class {
716	DPCOMPLEX = 9;
717	DOUBLE = 8;
718	COMPLEX = 7;
719	FLOAT = 6;
720	INT = 5;
721	UINT = 4;
722	SHORT = 3;
723	USHORT = 2;
724	CHAR = 1;
725	UCHAR = 0;
726	NOTSET = -1;
727
728	maxval fmt
729		= [
730			255,		// UCHAR
731			127,		// CHAR
732			65535,		// USHORT
733			32767,		// SHORT
734			4294967295,	// UINT
735			2147483647,	// INT
736			255,		// FLOAT
737			255,		// COMPLEX
738			255,		// DOUBLE
739			255			// DPCOMPLEX
740		] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX
741		= error (_ "bad value for BandFmt");
742}
743
744/* A lookup table.
745 */
746Table value = class
747	_Object {
748	_check_args = [
749		[value, "value", check_rectangular]
750	];
751
752	/* Extract a column.
753	 */
754	column n = map (extract n) value;
755
756	/* present col x: is there an x in column col
757	 */
758	present col x = member (column col) x;
759
760	/* Look on column from, return matching item in column to.
761	 */
762	lookup from to x
763		= value?n?to, n >= 0
764		= error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table")
765	{
766		n = index (equal x) (column from);
767	}
768}
769
770/* A two column lookup table with the first column a string and the second a
771 * thing. Used for representing various enums. Option_enum makes a selector
772 * from one of these.
773 */
774Enum value = class
775	Table value {
776	_check_args = [
777		[value, "value", check_enum]
778	]
779	{
780		check_enum = [is_enum, _ "is [[char, *]]"];
781		is_enum x =
782			is_rectangular x &&
783			is_listof is_string (map (extract 0) x);
784	}
785
786	// handy ... all the names and things as lists
787	names = this.column 0;
788	things = this.column 1;
789
790	// is a legal name or thing
791	has_name x = this.present 1 x;
792	has_thing x = this.present 0 x;
793
794	// map things to strings and back
795	get_name x = this.lookup 1 0 x;
796	get_thing x = this.lookup 0 1 x;
797}
798
799/* Type field.
800 */
801Image_type = class {
802	MULTIBAND = 0;
803	B_W = 1;
804	HISTOGRAM = 10;
805	XYZ = 12;
806	LAB = 13;
807	CMYK = 15;
808	LABQ = 16;
809	RGB = 17;
810	UCS = 18;
811	LCH = 19;
812	LABS = 21;
813	sRGB = 22;
814	YXY = 23;
815	FOURIER = 24;
816	RGB16 = 25;
817	GREY16 = 26;
818	ARRAY = 27;
819	scRGB = 28;
820
821	/* Table to get names <-> numbers.
822	 */
823	type_names = Enum [
824		$MULTIBAND => MULTIBAND,
825		$B_W => B_W,
826		$HISTOGRAM => HISTOGRAM,
827		$XYZ => XYZ,
828		$LAB => LAB,
829		$CMYK => CMYK,
830		$LABQ => LABQ,
831		$RGB => RGB,
832		$UCS => UCS,
833		$LCH => LCH,
834		$LABS => LABS,
835		$sRGB => sRGB,
836		$YXY => YXY,
837		$FOURIER => FOURIER,
838		$RGB16 => RGB16,
839		$GREY16 => GREY16,
840		$ARRAY => ARRAY,
841		$scRGB => scRGB
842	];
843
844	/* Table relating nip's colour space names and VIPS's Type numbers.
845	 * Options are generated from this, so match the order to the order in
846	 * the Colour menu.
847	 */
848	colour_spaces = Enum [
849		$sRGB => sRGB,
850		$scRGB => scRGB,
851		$Lab => LAB,
852		$LCh => LCH,
853		$XYZ => XYZ,
854		$Yxy => YXY,
855		$UCS => UCS
856	];
857
858	/* A slightly larger table ... the types of colorimetric image we can
859	 * have. Add mono, and the S and Q forms of LAB.
860	 */
861	image_colour_spaces = Enum [
862		$Mono => B_W,
863		$sRGB => sRGB,
864		$scRGB => scRGB,
865		$RGB16 => RGB16,
866		$GREY16 => GREY16,
867		$Lab => LAB,
868		$LabQ => LABQ,
869		$LabS => LABS,
870		$LCh => LCH,
871		$XYZ => XYZ,
872		$Yxy => YXY,
873		$UCS => UCS
874	];
875}
876
877/* Base image type. Simple layer over vips_image.
878 */
879Image value = class
880	_Object {
881	_check_args = [
882		[value, "value", check_image]
883	];
884
885	// fields from VIPS header
886	width = get_width value;
887	height = get_height value;
888	bands = get_bands value;
889	format = get_format value;
890	bits = get_bits value;
891	coding = get_coding value;
892	type = get_type value;
893	xres = get_header "Xres" value;
894	yres = get_header "Yres" value;
895	xoffset = get_header "Xoffset" value;
896	yoffset = get_header "Yoffset" value;
897	filename = get_header "filename" value;
898
899	// convenience ... the area our pixels occupy, as a rect
900	rect = Rect 0 0 width height;
901
902	// operator overloading
903	// (op Image Vector) done in Vector class
904	oo_binary_table op x = [
905		// handle image ++ constant here
906		[wrap join_result_image,
907			(has_real x || is_Vector x) &&
908			(op.op_name == "join" || op.op_name == "join'")],
909		[wrap ite_result_image,
910			op.op_name == "if_then_else"],
911		[wrap (op.fn this.value (get_image x)),
912			has_image x],
913		[wrap (op.fn this.value (get_number x)),
914			has_number x],
915		// if it's not a class on the RHS, handle here ... just apply and
916		// rewrap
917		[wrap (op.fn this.value x),
918			!is_class x]
919		// all other cases handled by other classes
920	] ++ super.oo_binary_table op x
921	{
922		// wrap the result with this
923		// x can be a non-image, eg. compare "Image v == []" vs.
924		// "Image v == 12"
925		wrap x
926			= x, op.type == Operator_type.COMPOUND ||
927				!is_image x
928			= this.Image x;
929
930		join_result_image
931			= value ++ new_stuff, op.op_name == "join"
932			= new_stuff ++ value
933		{
934			new_stuff = image_new width height new_bands
935				format
936				coding
937				Image_type.B_W x xoffset yoffset;
938			new_bands
939				= get_bands x, has_bands x
940				= 1;
941		}
942
943		[then_part, else_part] = x;
944
945		// get things about our output from inputs in this order
946		objects = [then_part, else_part, this];
947
948		// properties of our output image
949		target_bands = get_member_list has_bands get_bands objects;
950		target_type = get_member_list has_type get_type objects;
951
952		// if one of then/else is an image, get the target format from that
953		// otherwise, let the non-image objects set the target
954		target_format
955			= get_member_list has_format get_format x,
956				has_member_list has_format x
957			= NULL;
958
959		to_image x = to_image_size width height target_bands target_format x;
960
961		[then', else'] = map to_image x;
962
963		ite_result_image = image_set_type target_type
964			(if value then then' else else');
965	}
966
967	// FIXME ... yuk ... don't use operator hints, just always rewrap if
968	// we have an image result
969	// forced on us by things like abs:
970	// 	abs Vector -> real
971	//	abs Image -> Image
972	// does not fit well with COMPOUND/whatever scheme
973	oo_unary_table op = [
974		[this.Image result,
975			is_image result],
976		[result,
977			true]
978	] ++ super.oo_unary_table op
979	{
980		result = op.fn this.value;
981	}
982}
983
984/* Construct an image from a file.
985 */
986Image_file filename = class
987	Image value {
988	_check_args = [
989		[filename, "filename", check_string]
990	];
991
992	value = vips_image filename;
993}
994
995Region image left top width height = class
996	Image value {
997	_check_args = [
998		[image, "Image", check_Image],
999		[left, "left", check_real],
1000		[top, "top", check_real],
1001		[width, "width", check_preal],
1002		[height, "height", check_preal]
1003	];
1004
1005	// a rect for our coordinates
1006	// region.rect gets the rect for the extracted image
1007	region_rect = Rect left top width height;
1008
1009	// we need to always succeed ... value is our enclosing image if we're
1010	// out of bounds
1011	value
1012		= extract_area left top width height image.value,
1013			image.rect.includes_rect region_rect
1014		= image.value;
1015}
1016
1017Area image left top width height = class
1018	scope.Region image left top width height {
1019	Region image left top width height
1020		= this.Area image left top width height;
1021}
1022
1023Arrow image left top width height = class
1024	scope.Rect left top width height {
1025	_check_args = [
1026		[image, "Image", check_Image],
1027		[left, "left", check_real],
1028		[top, "top", check_real],
1029		[width, "width", check_real],
1030		[height, "height", check_real]
1031	];
1032
1033	Rect l t w h = this.Arrow image l t w h;
1034}
1035
1036HGuide image top = class
1037	scope.Arrow image image.rect.left top image.width 0 {
1038	Arrow image left top width height = this.HGuide image top;
1039}
1040
1041VGuide image left = class
1042	scope.Arrow image left image.rect.top 0 image.height {
1043	Arrow image left top width height = this.VGuide image left;
1044}
1045
1046Mark image left top = class
1047	scope.Arrow image left top 0 0 {
1048	Arrow image left top width height = this.Mark image left top;
1049}
1050
1051// convenience functions: ... specify position as [0 .. 1)
1052
1053Region_relative image u v w h
1054	= Region image
1055		(image.width * u)
1056		(image.height * v)
1057		(image.width * w)
1058		(image.height * h);
1059
1060Area_relative image u v w h
1061	= Area image
1062		(image.width * u)
1063		(image.height * v)
1064		(image.width * w)
1065		(image.height * h);
1066
1067Arrow_relative image u v w h
1068	= Arrow image
1069		(image.width * u)
1070		(image.height * v)
1071		(image.width * w)
1072		(image.height * h);
1073
1074VGuide_relative image v
1075	= VGuide image (image.height * v);
1076
1077HGuide_relative image u
1078	= HGuide image (image.width * u);
1079
1080Mark_relative image u v
1081	= Mark image
1082		(image.width * u)
1083		(image.height * v);
1084
1085Kernel_type = class {
1086	NEAREST_NEIGHBOUR = 0;
1087	LINEAR = 1;
1088	CUBIC = 2;
1089	MITCHELL = 3;
1090	LANCZOS2 = 4;
1091	LANCZOS3 = 5;
1092
1093	// Should introspect to get the list of interpolators :-(
1094	// We can "dir" on VipsInterpolate to get a list of them, but we
1095	// can't get i18n'd descriptions until we have more
1096	// introspection stuff in nip2.
1097
1098	/* Table to map kernel numbers to descriptive strings
1099	 */
1100	descriptions = [
1101		_ "Nearest neighbour",
1102		_ "Linear",
1103		_ "Cubic",
1104		_ "Mitchell",
1105		_ "Lanczos, two lobes",
1106		_ "Lanczos, three lobes"
1107	];
1108
1109	/* And to vips enum nicknames.
1110	 */
1111	types = [
1112		"nearest",
1113		"linear",
1114		"cubic",
1115		"mitchell",
1116		"lanczos2",
1117		"lanczos3"
1118	];
1119}
1120
1121Kernel type = class {
1122	value = Kernel_type.types?type;
1123}
1124
1125Kernel_linear = Kernel Kernel_type.LINEAR;
1126
1127Kernel_picker default = class
1128	Kernel kernel.value {
1129	_vislevel = 2;
1130
1131	kernel = Option "Kernel" Kernel_type.descriptions default;
1132}
1133
1134Interpolate_type = class {
1135	NEAREST_NEIGHBOUR = 0;
1136	BILINEAR = 1;
1137	BICUBIC = 2;
1138	LBB = 3;
1139	NOHALO = 4;
1140	VSQBS = 5;
1141
1142	// Should introspect to get the list of interpolators :-(
1143	// We can "dir" on VipsInterpolate to get a list of them, but we
1144	// can't get i18n'd descriptions until we have more
1145	// introspection stuff in nip2.
1146
1147	/* Table to map interpol numbers to descriptive strings
1148	 */
1149	descriptions = [
1150		_ "Nearest neighbour",
1151		_ "Bilinear",
1152		_ "Bicubic",
1153		_ "Upsize: reduced halo bicubic (LBB)",
1154		_ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)",
1155		_ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)"
1156	];
1157
1158	/* And to vips type names.
1159	 */
1160	types = [
1161		"VipsInterpolateNearest",
1162		"VipsInterpolateBilinear",
1163		"VipsInterpolateBicubic",
1164		"VipsInterpolateLbb",
1165		"VipsInterpolateNohalo",
1166		"VipsInterpolateVsqbs"
1167	];
1168}
1169
1170Interpolate type options = class {
1171	value = vips_object_new Interpolate_type.types?type [] options;
1172}
1173
1174Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR [];
1175
1176Interpolate_picker default = class
1177	Interpolate interp.value [] {
1178	_vislevel = 2;
1179
1180	interp = Option "Interpolation" Interpolate_type.descriptions default;
1181}
1182
1183Render_intent = class {
1184	PERCEPTUAL = 0;
1185	RELATIVE = 1;
1186	SATURATION = 2;
1187	ABSOLUTE = 3;
1188
1189	/* Table to get names <-> numbers.
1190	 */
1191	names = Enum [
1192		_ "Perceptual" => PERCEPTUAL,
1193		_ "Relative" => RELATIVE,
1194		_ "Saturation" => SATURATION,
1195		_ "Absolute" => ABSOLUTE
1196	];
1197}
1198
1199// abstract base class for toolkit menus
1200Menu = class {}
1201
1202// a "----" line in a menu
1203Menuseparator = class Menu {}
1204
1205// abstract base class for items in menus
1206Menuitem label tooltip = class Menu {}
1207
1208Menupullright label tooltip = class Menuitem label tooltip {}
1209
1210Menuaction label tooltip = class Menuitem label tooltip {}
1211
1212/* Plots.
1213 */
1214
1215Plot_style = class {
1216	POINT = 0;
1217	LINE = 1;
1218	SPLINE = 2;
1219	BAR = 3;
1220
1221	names = Enum [
1222		_ "Point" => POINT,
1223		_ "Line" => LINE,
1224		_ "Spline" => SPLINE,
1225		_ "Bar" => BAR
1226	];
1227}
1228
1229Plot_format = class {
1230	YYYY = 0;
1231	XYYY = 1;
1232	XYXY = 2;
1233
1234	names = Enum [
1235		_ "YYYY" => YYYY,
1236		_ "XYYY" => XYXY,
1237		_ "XYXY" => XYXY
1238	];
1239}
1240
1241Plot_type = class {
1242	/* Lots of Ys (ie. multiple line plots).
1243	 */
1244	YYYY = 0;
1245
1246	/* First column of matrix is X position, others are Ys (ie. multiple XY
1247	 * line plots, all with the same Xes).
1248	 */
1249	XYYY = 1;
1250
1251	/* Many independent XY plots.
1252	 */
1253	XYXY = 2;
1254}
1255
1256/* "options" is a list of ["key", value] pairs.
1257 */
1258Plot options value = class
1259	scope.Image value {
1260	Image value = this.Plot options value;
1261	to_image dpi = extract_bands 0 3
1262		(graph_export_image (to_real dpi) this);
1263}
1264
1265Plot_matrix options value = class
1266	Plot options (to_image value).value {
1267}
1268
1269Plot_histogram value = class
1270	scope.Plot [] value {
1271}
1272
1273Plot_xy value = class
1274	scope.Plot [$format => Plot_format.XYYY] value {
1275}
1276
1277/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate
1278 * empty slots, for example.
1279 */
1280NULL = class
1281	_Object {
1282	oo_binary_table op x = [
1283		// the only operation we allow is equality .. use pointer equality,
1284		// this lets us test a == NULL and a != NULL
1285		[this === x,
1286			op.type == Operator_type.RELATIONAL &&
1287			op.op_name == "equal"],
1288		[this !== x,
1289			op.type == Operator_type.RELATIONAL &&
1290			op.op_name == "not_equal"]
1291	] ++ super.oo_binary_table op x;
1292}
1293
1294Blend_type = class {
1295	CLEAR = 0;
1296	SOURCE = 1;
1297	OVER = 2;
1298	IN = 3;
1299	OUT = 4;
1300	ATOP = 5;
1301	DEST = 6;
1302	DEST_OVER = 7;
1303	DEST_IN = 8;
1304	DEST_OUT = 9;
1305	DEST_ATOP = 10;
1306	XOR = 11;
1307	ADD = 12;
1308	SATURATE = 13;
1309	MULTIPLY = 14;
1310	SCREEN = 15;
1311	OVERLAY = 16;
1312	DARKEN = 17;
1313	LIGHTEN = 18;
1314	COLOUR_DODGE = 19;
1315	COLOUR_BURN = 20;
1316	HARD_LIGHT = 21;
1317	SOFT_LIGHT = 22;
1318	DIFFERENCE = 23;
1319	EXCLUSION = 24;
1320
1321	/* Table to map blend numbers to descriptive strings
1322	 */
1323	descriptions = [
1324		_ "Clear",
1325		_ "Source",
1326		_ "Over",
1327		_ "In",
1328		_ "Out",
1329		_ "Atop",
1330		_ "Dest",
1331		_ "Dest over",
1332		_ "Dest in",
1333		_ "Dest out",
1334		_ "Dest atop",
1335		_ "Xor",
1336		_ "Add",
1337		_ "Saturate",
1338		_ "Multiply",
1339		_ "Screen",
1340		_ "Overlay",
1341		_ "Darken",
1342		_ "Lighten",
1343		_ "Colour dodge",
1344		_ "Colour burn",
1345		_ "Hard light",
1346		_ "Soft light",
1347		_ "Difference",
1348		_ "Exclusion"
1349	];
1350
1351	/* And to vips enum nicknames.
1352	 */
1353	types = Enum [
1354		$clear => "clear",
1355		$source => "source",
1356		$over => "over",
1357		$in => "in",
1358		$out => "out",
1359		$atop => "atop",
1360		$dest => "dest",
1361		$dest_over => "dest_over",
1362		$dest_in => "dest_in",
1363		$dest_out => "dest_out",
1364		$dest_atop => "dest_atop",
1365		$xor => "xor",
1366		$add => "add",
1367		$saturate => "saturate",
1368		$multiply => "multiply",
1369		$screen => "screen",
1370		$overlay => "overlay",
1371		$darken => "darken",
1372		$lighten => "lighten",
1373		$colour_dodge => "colour_dodge",
1374		$colour_burn => "colour_burn",
1375		$hard_light => "hard_light",
1376		$soft_light => "soft_light",
1377		$difference => "difference",
1378		$exclusion => "exclusion"
1379	];
1380}
1381
1382Blend type = class {
1383	value = Blend_type.types?type;
1384}
1385
1386Blend_over = Blend Blend_type.OVER;
1387
1388Blend_picker default = class
1389	Blend blend.value {
1390	_vislevel = 2;
1391
1392	blend = Option "Blend" Blend_type.descriptions default;
1393}
1394
1395Combine_type = class {
1396	MAX = 0;
1397	SUM = 1;
1398	MIN = 2;
1399
1400	enum = Enum [
1401		_ "Maximum" => MAX,
1402		_ "Sum" => SUM,
1403		_ "Minimum" => MIN
1404	];
1405}
1406
1407Combine type = class {
1408	value = Combine_type.enum.names?type;
1409}
1410
1411Combine_sum = Combine Combine_type.SUM;
1412
1413Combine_picker default = Option "Combine" Combine_type.enum.names default;
1414