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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
194}
195
196/* A ticking clock.
197 */
198Clock interval value = class
199	scope.Real value {
200	_check_args = [
201		[interval, "interval", check_real]
202	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super._check_args;
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	] ++ super.oo_binary_table op x
369	{
370		mul = im_matmul this x;
371		mul' = im_matmul x this;
372		div = im_matmul this (im_matinv x);
373		div' = im_matmul x (im_matinv this);
374		inv = im_matinv this;
375		sq = im_matmul this this;
376		op' = oo_converse op;
377	}
378
379	oo_unary_table op = [
380		[this.Matrix_base (map_unaryl op.fn this.value),
381			op.type == Operator_type.ARITHMETIC],
382		[this.Matrix_base (op.fn this.value),
383			op.type == Operator_type.COMPOUND_REWRAP],
384		[op.fn this.value,
385			true]
386	] ++ super.oo_unary_table op;
387}
388
389/* How to display a matrix: text, sliders, toggles, or text plus scale/offset.
390 */
391Matrix_display = class {
392        text = 0;
393        slider = 1;
394        toggle = 2;
395        text_scale_offset = 3;
396
397        is_display = member [text, slider, toggle, text_scale_offset];
398}
399
400/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add
401 * a display type as well to control how the widget renders.
402 */
403Matrix_vips value scale offset filename display = class
404	scope.Matrix_base value {
405	_check_args = [
406		[scale, "scale", check_real],
407		[offset, "offset", check_real],
408		[filename, "filename", check_string],
409		[display, "display", check_matrix_display]
410	] ++ super._check_args;
411
412	Matrix_base x = this.Matrix_vips x scale offset filename display;
413}
414
415/* A plain 'ol matrix which can be passed to VIPS.
416 */
417Matrix value = class
418	Matrix_vips value 1 0 "" Matrix_display.text {}
419
420/* Specialised constructors ... for convolutions, recombinations and
421 * morphologies.
422 */
423Matrix_con scale offset value = class
424	Matrix_vips value scale offset "" Matrix_display.text_scale_offset {};
425
426Matrix_rec value = class
427	Matrix_vips value 1 0 "" Matrix_display.slider {};
428
429Matrix_mor value = class
430	Matrix_vips value 1 0 "" Matrix_display.toggle {};
431
432Matrix_file filename = (im_read_dmask @ expand @ search) filename;
433
434/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc)
435 */
436Colour colour_space value = class
437	scope.Vector value {
438	_check_args = [
439		[colour_space, "colour_space", check_colour_space]
440	] ++ super._check_args;
441	_check_all = [
442		[is_list_len 3 value, "len value == 3"]
443	] ++ super._check_all;
444
445	Vector x = this.Colour colour_space x;
446
447	// make a colour-ish thing from an image
448	// back to Colour if we have another 3 band image
449	// to a vector if bands > 1
450	// to a number otherwise
451	itoc im
452		= this.Colour nip_type (to_matrix im).value?0,
453			bands == 3
454		= scope.Vector (map mean (bandsplit im)),
455			bands > 1
456		= mean im
457	{
458		type = get_header "Type" im;
459		bands = get_header "Bands" im;
460		nip_type = Image_type.colour_spaces.lookup 1 0 type;
461	}
462
463	// methods
464	oo_binary_table op x = [
465		[itoc (op.fn
466			((float) (to_image this).value)
467			((float) (to_image x).value)),
468			// here REWRAP means go via image
469			op.type == Operator_type.COMPOUND_REWRAP]
470	] ++ super.oo_binary_table op x;
471
472	oo_unary_table op = [
473		[itoc (op.fn ((float) (to_image this).value)),
474			op.type == Operator_type.COMPOUND_REWRAP]
475	] ++ super.oo_unary_table op;
476}
477
478// a subclass with widgets for picking a space and value
479Colour_picker default_colour default_value = class
480	Colour space.value_name colour.expr {
481	_vislevel = 3;
482
483	space = Option_enum Image_type.colour_spaces "Colour space" default_colour;
484	colour = Expression "Colour value" default_value;
485
486	Colour_edit colour_space value =
487		Colour_picker colour_space value;
488}
489
490/* Base scale type.
491 */
492Scale caption from to value = class
493	scope.Real value {
494	_check_args = [
495		[caption, "caption", check_string],
496		[from, "from", check_real],
497		[to, "to", check_real]
498	] ++ super._check_args;
499	_check_all = [
500		[from < to, "from < to"]
501	] ++ super._check_all;
502
503	Real x = this.Scale caption from to x;
504
505	// methods
506	oo_binary_table op x = [
507		[this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to)
508			(op.fn this.value x.value),
509			is_Scale x &&
510			op.type == Operator_type.ARITHMETIC],
511		[this.Scale caption (op.fn this.from x) (op.fn this.to x)
512			(op.fn this.value x),
513			is_real x &&
514			op.type == Operator_type.ARITHMETIC]
515	] ++ super.oo_binary_table op x;
516}
517
518/* Base toggle type.
519 */
520Toggle caption value = class
521	scope.Bool value {
522	_check_args = [
523		[caption, "caption", check_string],
524		[value, "value", check_bool]
525	] ++ super._check_args;
526
527	Bool x = this.Toggle caption x;
528}
529
530/* Base option type.
531 */
532Option caption labels value = class
533	scope.Real value {
534	_check_args = [
535		[caption, "caption", check_string],
536		[labels, "labels", check_string_list],
537		[value, "value", check_uint]
538	] ++ super._check_args;
539}
540
541Option_enum enum caption value_name = class
542	Option caption enum.names (index (equal value_name) enum.names) {
543	// corresponding thing
544	value_thing = enum.get_thing value_name;
545
546	Option_edit caption labels value
547		= this.Option_enum enum caption (enum.names ? value);
548}
549
550/* A rectangle. width and height can be -ve.
551 */
552Rect left top width height = class
553	_Object {
554	_check_args = [
555		[left, "left", check_real],
556		[top, "top", check_real],
557		[width, "width", check_real],
558		[height, "height", check_real]
559	] ++ super._check_args;
560
561	// derived
562	right = left + width;
563	bottom = top + height;
564
565	oo_binary_table op x = [
566		[equal x,
567			is_Rect x &&
568			(op.op_name == "equal" || op.op_name == "equal'")],
569		[!equal x,
570			is_Rect x &&
571			(op.op_name == "not_equal" ||
572				op.op_name == "not_equal'")],
573
574		// binops with a complex are the same as (comp op comp)
575		[oo_binary_function op this (Rect (re x) (im x) 0 0),
576			is_complex x],
577
578		// all others are just pairwise
579		[this.Rect left' top' width' height',
580			is_Rect x &&
581			op.type == Operator_type.ARITHMETIC],
582		[this.Rect left'' top'' width'' height'',
583			has_number x &&
584			op.type == Operator_type.ARITHMETIC]
585	] ++ super.oo_binary_table op x
586	{
587		left' = op.fn left x.left;
588		top' = op.fn top x.top;
589		width' = op.fn width x.width;
590		height' = op.fn height x.height;
591
592		left'' = op.fn left x';
593		top'' = op.fn top x';
594		width'' = op.fn width x';
595		height'' = op.fn height x';
596		x' = get_number x;
597	}
598
599	oo_unary_table op = [
600		// arithmetic uops just map
601		[this.Rect left' top' width' height',
602			op.type == Operator_type.ARITHMETIC],
603
604		// compound uops are just like ops on complex
605		// do (width, height) so thing like abs(Arrow) work as you'd expect
606		[op.fn (width, height),
607			op.type == Operator_type.COMPOUND]
608	] ++ super.oo_unary_table op
609	{
610		left' = op.fn left;
611		top' = op.fn top;
612		width' = op.fn width;
613		height' = op.fn height;
614	}
615
616	// empty? ie. contains no pixels
617	is_empty = width == 0 || height == 0;
618
619	// normalised version, ie. make width/height +ve and flip the origin
620	nleft
621		= left + width, width < 0
622		= left;
623	ntop
624		= top + height, height < 0
625		= top;
626	nwidth = abs width;
627	nheight = abs height;
628	nright = nleft + nwidth;
629	nbottom = ntop + nheight;
630
631	equal x = left == x.left && top == x.top &&
632		  width == x.width && height == x.height;
633
634	// contains a point?
635	includes_point x y
636		= nleft <= x && x <= nright && ntop <= y && y <= nbottom;
637
638	// contains a rect? just test top left and bottom right points
639	includes_rect r
640		= includes_point r.nleft r.ntop &&
641			includes_point r.nright r.nbottom;
642
643	// bounding box of two rects
644	// if either is empty, can just return the other
645	union r
646		= r, is_empty
647		= this, r.is_empty
648		= Rect left' top' width' height'
649	{
650		left' = min_pair nleft r.nleft;
651		top' = min_pair ntop r.ntop;
652		width' = max_pair nright r.nright - left';
653		height' = max_pair nbottom r.nbottom - top';
654	}
655
656	// intersection of two rects ... empty rect if no intersection
657	intersect r
658		= Rect left' top' width'' height''
659	{
660		left' = max_pair nleft r.nleft;
661		top' = max_pair ntop r.ntop;
662		width' = min_pair nright r.nright - left';
663		height' = min_pair nbottom r.nbottom - top';
664		width''
665			= width', width > 0
666			= 0;
667		height''
668			= height', height > 0
669			= 0;
670	}
671
672	// expand/collapse by n pixels
673	margin_adjust n
674		= Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n);
675}
676
677/* Values for Compression field in image.
678 */
679Image_compression = class {
680	NONE = 0;
681	NO_COMPRESSION = 0;
682	TCSF_COMPRESSION = 1;
683	JPEG_COMPRESSION = 2;
684	LABPACK_COMPRESSED = 3;
685	RGB_COMPRESSED = 4;
686	LUM_COMPRESSED = 5;
687}
688
689/* Values for Coding field in image.
690 */
691Image_coding = class {
692	NONE = 0;
693	NOCODING = 0;
694	COLQUANT = 1;
695	LABPACK = 2;
696}
697
698/* Values for BandFmt field in image.
699 */
700Image_format = class {
701	DPCOMPLEX = 9;
702	DOUBLE = 8;
703	COMPLEX = 7;
704	FLOAT = 6;
705	INT = 5;
706	UINT = 4;
707	SHORT = 3;
708	USHORT = 2;
709	CHAR = 1;
710	UCHAR = 0;
711	NOTSET = -1;
712
713	maxval fmt
714		= [
715			255,		// UCHAR
716			127,		// CHAR
717			65535,		// USHORT
718			32767,		// SHORT
719			4294967295,	// UINT
720			2147483647,	// INT
721			255,		// FLOAT
722			255,		// COMPLEX
723			255,		// DOUBLE
724			255			// DPCOMPLEX
725		] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX
726		= error (_ "bad value for BandFmt");
727}
728
729/* A lookup table.
730 */
731Table value = class
732	_Object {
733	_check_args = [
734		[value, "value", check_rectangular]
735	] ++ super._check_args;
736
737	/* present col x: is there an x in column col
738	 */
739	present col x = member (map (extract col) value) x;
740
741	/* Look on column from, return matching item in column to.
742	 */
743	lookup from to x
744		= value?n?to, n >= 0
745		= error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table")
746	{
747		n = index (equal x) (map (extract from) value);
748	}
749}
750
751/* A two column lookup table with the first column a string and the second a
752 * thing. Used for representing various enums. Option_enum makes a selector
753 * from one of these.
754 */
755Enum value = class
756	Table value {
757	_check_args = [
758		[value, "value", check_enum]
759	] ++ super._check_args
760	{
761		check_enum = [is_enum, _ "is [[char, *]]"];
762		is_enum x =
763			is_rectangular x &&
764			is_listof is_string (map (extract 0) x);
765	}
766
767	// handy ... all the names and things as lists
768	names = map (extract 0) value;
769	things = map (extract 1) value;
770
771	// is a legal name or thing
772	has_name x = this.present 1 x;
773	has_thing x = this.present 0 x;
774
775	// map things to strings and back
776	get_name x = this.lookup 1 0 x;
777	get_thing x = this.lookup 0 1 x;
778}
779
780/* Type field.
781 */
782Image_type = class {
783	MULTIBAND = 0;
784	B_W = 1;
785	LUMINANCE = 2;
786	XRAY = 3;
787	IR = 4;
788	YUV = 5;
789	RED_ONLY = 6;
790	GREEN_ONLY = 7;
791	BLUE_ONLY = 8;
792	POWER_SPECTRUM = 9;
793	HISTOGRAM = 10;
794	LUT = 11;
795	XYZ = 12;
796	LAB = 13;
797	CMC = 14;
798	CMYK = 15;
799	LABQ = 16;
800	RGB = 17;
801	UCS = 18;
802	LCH = 19;
803	LABS = 21;
804	sRGB = 22;
805	YXY = 23;
806	FOURIER = 24;
807	RGB16 = 25;
808	GREY16 = 26;
809
810	/* Table to get names <-> numbers.
811	 */
812	type_names = Enum [
813		$MULTIBAND => MULTIBAND,
814		$B_W => B_W,
815		$LUMINANCE => LUMINANCE,
816		$XRAY => XRAY,
817		$IR => IR,
818		$YUV => YUV,
819		$RED_ONLY => RED_ONLY,
820		$GREEN_ONLY => GREEN_ONLY,
821		$BLUE_ONLY => BLUE_ONLY,
822		$POWER_SPECTRUM => POWER_SPECTRUM,
823		$HISTOGRAM => HISTOGRAM,
824		$LUT => LUT,
825		$XYZ => XYZ,
826		$LAB => LAB,
827		$CMC => CMC,
828		$CMYK => CMYK,
829		$LABQ => LABQ,
830		$RGB => RGB,
831		$UCS => UCS,
832		$LCH => LCH,
833		$LABS => LABS,
834		$sRGB => sRGB,
835		$YXY => YXY,
836		$FOURIER => FOURIER,
837		$RGB16 => RGB16,
838		$GREY16 => GREY16
839	];
840
841	/* Table relating nip's colour space names and VIPS's Type numbers.
842	 * Options generated from this, so match the order to the order in the
843	 * Colour menu.
844	 */
845	colour_spaces = Enum [
846		$sRGB => sRGB,
847		$Lab => LAB,
848		$LCh => LCH,
849		$XYZ => XYZ,
850		$Yxy => YXY,
851		$UCS => UCS
852	];
853
854	/* A slightly larger table ... the types of colorimetric image we can
855	 * have. Add mono, and the S and Q forms of LAB.
856	 */
857	image_colour_spaces = Enum [
858		$Mono => B_W,
859		$sRGB => sRGB,
860		$RGB16 => RGB16,
861		$GREY16 => GREY16,
862		$Lab => LAB,
863		$LabQ => LABQ,
864		$LabS => LABS,
865		$LCh => LCH,
866		$XYZ => XYZ,
867		$Yxy => YXY,
868		$UCS => UCS
869	];
870}
871
872/* Base image type. Simple layer over vips_image.
873 */
874Image value = class
875	_Object {
876	_check_args = [
877		[value, "value", check_image]
878	] ++ super._check_args;
879
880	// fields from VIPS header
881	width = get_width value;
882	height = get_height value;
883	bands = get_bands value;
884	format = get_format value;
885	bits = get_bits value;
886	coding = get_coding value;
887	type = get_type value;
888	xres = get_header "Xres" value;
889	yres = get_header "Yres" value;
890	xoffset = get_header "Xoffset" value;
891	yoffset = get_header "Yoffset" value;
892	filename = get_header "filename" value;
893
894	// convenience ... the area our pixels occupy, as a rect
895	rect = Rect 0 0 width height;
896
897	// operator overloading
898	// (op Image Vector) done in Vector class
899	oo_binary_table op x = [
900		// handle image ++ constant here
901		[wrap join_result_image,
902			(has_real x || is_Vector x) &&
903			(op.op_name == "join" || op.op_name == "join'")],
904		// image ++ image is slightly different ... we want to
905		// sizealike, but we must not bandalike
906		[wrap
907			(op.fn (get_image resized?0) (get_image resized?1)),
908			has_image x &&
909			(op.op_name == "join" || op.op_name == "join'")],
910		[wrap ite_result_image,
911			op.op_name == "if_then_else"],
912		// arithmetic and reational binops between image resize
913		// and band_alike images to match
914		[wrap
915			(op.fn (get_image rebanded?0) (get_image rebanded?1)),
916			has_image x &&
917			(op.type == Operator_type.ARITHMETIC ||
918				op.type == Operator_type.RELATIONAL)],
919		// other op types don't resize
920		[wrap (op.fn this.value (get_image x)),
921			has_image x],
922		[wrap (op.fn this.value (get_number x)),
923			has_number x],
924		// if it's not a class on the RHS, handle here ... just apply and
925		// rewrap
926		[wrap (op.fn this.value x),
927			!is_class x]
928		// all other cases handled by other classes
929	] ++ super.oo_binary_table op x
930	{
931		// wrap the result with this
932		// x can be a non-image, eg. compare "Image v == []" vs. "Image v ==
933		// 12"
934		wrap x
935			= x, op.type == Operator_type.COMPOUND ||
936				!is_image x
937			= this.Image x;
938
939		join_result_image
940			= value ++ new_stuff, op.op_name == "join"
941			= new_stuff ++ value
942		{
943			new_stuff = image_new width height new_bands
944				format
945				coding
946				Image_type.B_W x xoffset yoffset;
947			new_bands
948				= get_bands x, has_bands x
949				= 1;
950		}
951
952		[then_part, else_part] = x;
953
954		// get things about our output from inputs in this order
955		objects = [then_part, else_part, this];
956
957		// properties of our output image
958		target_bands = get_member_list has_bands get_bands objects;
959		target_type = get_member_list has_type get_type objects;
960
961		// if one of then/else is an image, get the target format from that
962		// otherwise, let the non-image objects set the target
963		target_format
964			= get_member_list has_format get_format x,
965				has_member_list has_format x
966			= [];
967
968		to_image x
969			= x, is_image x
970			= x.value, is_Image x
971			= clip2fmt target_format im, target_format != []
972			= im
973		{
974			im = im_black width height target_bands + x;
975		}
976
977		[if_size, then_size, else_size] =
978			size_alike (value : formats_alike (map to_image x));
979
980		ite_result_image = image_set_type target_type
981			(if if_size then then_size else else_size);
982
983		resized = size_alike [this, x];
984		rebanded = bands_alike resized;
985	}
986
987	// FIXME ... yuk ... don't use operator hints, just always rewrap if
988	// we have an image result
989	// forced on us by things like abs:
990	// 	abs Vector -> real
991	//	abs Image -> Image
992	// does not fit well with COMPOUND/whatever scheme
993	oo_unary_table op = [
994		[this.Image result,
995			is_image result],
996		[result,
997			true]
998	] ++ super.oo_unary_table op
999	{
1000		result = op.fn this.value;
1001	}
1002}
1003
1004/* Construct an image from a file.
1005 */
1006Image_file filename = class
1007	Image value {
1008	_check_args = [
1009		[filename, "filename", check_string]
1010	] ++ super._check_args;
1011
1012	value = vips_image filename;
1013}
1014
1015Region image left top width height = class
1016	Image value {
1017	_check_args = [
1018		[image, "Image", check_Image],
1019		[left, "left", check_real],
1020		[top, "top", check_real],
1021		[width, "width", check_preal],
1022		[height, "height", check_preal]
1023	] ++ super._check_args;
1024
1025	// a rect for our coordinates
1026	// region.rect gets the rect for the extracted image
1027	region_rect = Rect left top width height;
1028
1029	// we need to always succeed ... value is our enclosing image if we're
1030	// out of bounds
1031	value
1032		= extract_area left top width height image.value,
1033			image.rect.includes_rect region_rect
1034		= image.value;
1035}
1036
1037Area image left top width height = class
1038	scope.Region image left top width height {
1039	Region image left top width height
1040		= this.Area image left top width height;
1041}
1042
1043Arrow image left top width height = class
1044	scope.Rect left top width height {
1045	_check_args = [
1046		[image, "Image", check_Image],
1047		[left, "left", check_real],
1048		[top, "top", check_real],
1049		[width, "width", check_real],
1050		[height, "height", check_real]
1051	] ++ super._check_args;
1052
1053	Rect l t w h = this.Arrow image l t w h;
1054}
1055
1056HGuide image top = class
1057	scope.Arrow image image.rect.left top image.width 0 {
1058	Arrow image left top width height = this.HGuide image top;
1059}
1060
1061VGuide image left = class
1062	scope.Arrow image left image.rect.top 0 image.height {
1063	Arrow image left top width height = this.VGuide image left;
1064}
1065
1066Mark image left top = class
1067	scope.Arrow image left top 0 0 {
1068	Arrow image left top width height = this.Mark image left top;
1069}
1070
1071// convenience functions: ... specify position as [0 .. 1)
1072
1073Region_relative image u v w h
1074	= Region image
1075		(image.width * u)
1076		(image.height * v)
1077		(image.width * w)
1078		(image.height * h);
1079
1080Area_relative image u v w h
1081	= Area image
1082		(image.width * u)
1083		(image.height * v)
1084		(image.width * w)
1085		(image.height * h);
1086
1087Arrow_relative image u v w h
1088	= Arrow image
1089		(image.width * u)
1090		(image.height * v)
1091		(image.width * w)
1092		(image.height * h);
1093
1094VGuide_relative image v
1095	= VGuide image (image.height * v);
1096
1097HGuide_relative image u
1098	= HGuide image (image.width * u);
1099
1100Mark_relative image u v
1101	= Mark image
1102		(image.width * u)
1103		(image.height * v);
1104
1105Interpolate = class {
1106	NEAREST_NEIGHBOUR = 0;
1107	BILINEAR = 1;
1108	BICUBIC = 2;
1109
1110	/* Table to map interpol numbers to descriptive strings
1111	 */
1112	names = Enum [
1113		[_ "Nearest neighbour", NEAREST_NEIGHBOUR],
1114		[_ "Bilinear", BILINEAR],
1115		[_ "Bicubic", BICUBIC]
1116	];
1117}
1118
1119Render_intent = class {
1120	PERCEPTUAL = 0;
1121	RELATIVE = 1;
1122	SATURATION = 2;
1123	ABSOLUTE = 3;
1124
1125	/* Table to get names <-> numbers.
1126	 */
1127	names = Enum [
1128		[_ "Perceptual", PERCEPTUAL],
1129		[_ "Relative", RELATIVE],
1130		[_ "Saturation", SATURATION],
1131		[_ "Absolute", ABSOLUTE]
1132	];
1133}
1134
1135// abstract base class for toolkit menus
1136Menu = class {}
1137
1138// a "----" line in a menu
1139Menuseparator = class Menu {}
1140
1141// abstract base class for items in menus
1142Menuitem label tooltip = class Menu {}
1143
1144Menupullright label tooltip = class Menuitem label tooltip {}
1145
1146Menuaction label tooltip = class Menuitem label tooltip {}
1147
1148/* Plots.
1149 */
1150
1151Plot_style = class {
1152	POINT = 0;
1153	LINE = 1;
1154	SPLINE = 2;
1155	BAR = 3;
1156
1157	names = Enum [
1158		[_ "Point", POINT],
1159		[_ "Line", LINE],
1160		[_ "Spline", SPLINE],
1161		[_ "Bar", BAR]
1162	];
1163}
1164
1165Plot_format = class {
1166	YYYY = 0;
1167	XYYY = 1;
1168	XYXY = 2;
1169
1170	names = Enum [
1171		[_ "YYYY", YYYY],
1172		[_ "XYYY", XYXY],
1173		[_ "XYXY", XYXY]
1174	];
1175}
1176
1177Plot_type = class {
1178	/* Lots of Ys (ie. multiple line plots).
1179	 */
1180	YYYY = 0;
1181
1182	/* First column of matrix is X position, others are Ys (ie. multiple XY
1183	 * line plots, all with the same Xes).
1184	 */
1185	XYYY = 1;
1186
1187	/* Many independent XY plots.
1188	 */
1189	XYXY = 2;
1190}
1191
1192/* "options" is a list of ["key", value] pairs.
1193 */
1194Plot options value = class
1195	scope.Image value {
1196	Image value = this.Plot options value;
1197}
1198
1199Plot_matrix options value = class
1200	Plot options (to_image value).value {
1201}
1202
1203Plot_histogram value = class
1204	scope.Plot [] value {
1205}
1206
1207Plot_xy value = class
1208	scope.Plot [$format => Plot_format.XYYY] value {
1209}
1210
1211/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate
1212 * empty slots, for example.
1213 */
1214NULL = class
1215	_Object {
1216	oo_binary_table op x = [
1217		// the only operation we allow is equality .. use pointer equality,
1218		// this lets us test a == NULL and a != NULL
1219		[this === x,
1220			op.type == Operator_type.RELATIONAL &&
1221			op.op_name == "equal"],
1222		[this !== x,
1223			op.type == Operator_type.RELATIONAL &&
1224			op.op_name == "not_equal"]
1225	] ++ super.oo_binary_table op x;
1226}
1227