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
820	/* Table to get names <-> numbers.
821	 */
822	type_names = Enum [
823		$MULTIBAND => MULTIBAND,
824		$B_W => B_W,
825		$HISTOGRAM => HISTOGRAM,
826		$XYZ => XYZ,
827		$LAB => LAB,
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		$ARRAY => ARRAY
840	];
841
842	/* Table relating nip's colour space names and VIPS's Type numbers.
843	 * Options generated from this, so match the order to the order in the
844	 * Colour menu.
845	 */
846	colour_spaces = Enum [
847		$sRGB => sRGB,
848		$Lab => LAB,
849		$LCh => LCH,
850		$XYZ => XYZ,
851		$Yxy => YXY,
852		$UCS => UCS
853	];
854
855	/* A slightly larger table ... the types of colorimetric image we can
856	 * have. Add mono, and the S and Q forms of LAB.
857	 */
858	image_colour_spaces = Enum [
859		$Mono => B_W,
860		$sRGB => sRGB,
861		$RGB16 => RGB16,
862		$GREY16 => GREY16,
863		$Lab => LAB,
864		$LabQ => LABQ,
865		$LabS => LABS,
866		$LCh => LCH,
867		$XYZ => XYZ,
868		$Yxy => YXY,
869		$UCS => UCS
870	];
871}
872
873/* Base image type. Simple layer over vips_image.
874 */
875Image value = class
876	_Object {
877	_check_args = [
878		[value, "value", check_image]
879	];
880
881	// fields from VIPS header
882	width = get_width value;
883	height = get_height value;
884	bands = get_bands value;
885	format = get_format value;
886	bits = get_bits value;
887	coding = get_coding value;
888	type = get_type value;
889	xres = get_header "Xres" value;
890	yres = get_header "Yres" value;
891	xoffset = get_header "Xoffset" value;
892	yoffset = get_header "Yoffset" value;
893	filename = get_header "filename" value;
894
895	// convenience ... the area our pixels occupy, as a rect
896	rect = Rect 0 0 width height;
897
898	// operator overloading
899	// (op Image Vector) done in Vector class
900	oo_binary_table op x = [
901		// handle image ++ constant here
902		[wrap join_result_image,
903			(has_real x || is_Vector x) &&
904			(op.op_name == "join" || op.op_name == "join'")],
905		[wrap ite_result_image,
906			op.op_name == "if_then_else"],
907		[wrap (op.fn this.value (get_image x)),
908			has_image x],
909		[wrap (op.fn this.value (get_number x)),
910			has_number x],
911		// if it's not a class on the RHS, handle here ... just apply and
912		// rewrap
913		[wrap (op.fn this.value x),
914			!is_class x]
915		// all other cases handled by other classes
916	] ++ super.oo_binary_table op x
917	{
918		// wrap the result with this
919		// x can be a non-image, eg. compare "Image v == []" vs.
920		// "Image v == 12"
921		wrap x
922			= x, op.type == Operator_type.COMPOUND ||
923				!is_image x
924			= this.Image x;
925
926		join_result_image
927			= value ++ new_stuff, op.op_name == "join"
928			= new_stuff ++ value
929		{
930			new_stuff = image_new width height new_bands
931				format
932				coding
933				Image_type.B_W x xoffset yoffset;
934			new_bands
935				= get_bands x, has_bands x
936				= 1;
937		}
938
939		[then_part, else_part] = x;
940
941		// get things about our output from inputs in this order
942		objects = [then_part, else_part, this];
943
944		// properties of our output image
945		target_bands = get_member_list has_bands get_bands objects;
946		target_type = get_member_list has_type get_type objects;
947
948		// if one of then/else is an image, get the target format from that
949		// otherwise, let the non-image objects set the target
950		target_format
951			= get_member_list has_format get_format x,
952				has_member_list has_format x
953			= NULL;
954
955		to_image x = to_image_size width height target_bands target_format x;
956
957		[then', else'] = map to_image x;
958
959		ite_result_image = image_set_type target_type
960			(if value then then' else else');
961	}
962
963	// FIXME ... yuk ... don't use operator hints, just always rewrap if
964	// we have an image result
965	// forced on us by things like abs:
966	// 	abs Vector -> real
967	//	abs Image -> Image
968	// does not fit well with COMPOUND/whatever scheme
969	oo_unary_table op = [
970		[this.Image result,
971			is_image result],
972		[result,
973			true]
974	] ++ super.oo_unary_table op
975	{
976		result = op.fn this.value;
977	}
978}
979
980/* Construct an image from a file.
981 */
982Image_file filename = class
983	Image value {
984	_check_args = [
985		[filename, "filename", check_string]
986	];
987
988	value = vips_image filename;
989}
990
991Region image left top width height = class
992	Image value {
993	_check_args = [
994		[image, "Image", check_Image],
995		[left, "left", check_real],
996		[top, "top", check_real],
997		[width, "width", check_preal],
998		[height, "height", check_preal]
999	];
1000
1001	// a rect for our coordinates
1002	// region.rect gets the rect for the extracted image
1003	region_rect = Rect left top width height;
1004
1005	// we need to always succeed ... value is our enclosing image if we're
1006	// out of bounds
1007	value
1008		= extract_area left top width height image.value,
1009			image.rect.includes_rect region_rect
1010		= image.value;
1011}
1012
1013Area image left top width height = class
1014	scope.Region image left top width height {
1015	Region image left top width height
1016		= this.Area image left top width height;
1017}
1018
1019Arrow image left top width height = class
1020	scope.Rect left top width height {
1021	_check_args = [
1022		[image, "Image", check_Image],
1023		[left, "left", check_real],
1024		[top, "top", check_real],
1025		[width, "width", check_real],
1026		[height, "height", check_real]
1027	];
1028
1029	Rect l t w h = this.Arrow image l t w h;
1030}
1031
1032HGuide image top = class
1033	scope.Arrow image image.rect.left top image.width 0 {
1034	Arrow image left top width height = this.HGuide image top;
1035}
1036
1037VGuide image left = class
1038	scope.Arrow image left image.rect.top 0 image.height {
1039	Arrow image left top width height = this.VGuide image left;
1040}
1041
1042Mark image left top = class
1043	scope.Arrow image left top 0 0 {
1044	Arrow image left top width height = this.Mark image left top;
1045}
1046
1047// convenience functions: ... specify position as [0 .. 1)
1048
1049Region_relative image u v w h
1050	= Region image
1051		(image.width * u)
1052		(image.height * v)
1053		(image.width * w)
1054		(image.height * h);
1055
1056Area_relative image u v w h
1057	= Area image
1058		(image.width * u)
1059		(image.height * v)
1060		(image.width * w)
1061		(image.height * h);
1062
1063Arrow_relative image u v w h
1064	= Arrow image
1065		(image.width * u)
1066		(image.height * v)
1067		(image.width * w)
1068		(image.height * h);
1069
1070VGuide_relative image v
1071	= VGuide image (image.height * v);
1072
1073HGuide_relative image u
1074	= HGuide image (image.width * u);
1075
1076Mark_relative image u v
1077	= Mark image
1078		(image.width * u)
1079		(image.height * v);
1080
1081Interpolate_type = class {
1082	NEAREST_NEIGHBOUR = 0;
1083	BILINEAR = 1;
1084	BICUBIC = 2;
1085	LBB = 3;
1086	NOHALO = 4;
1087	VSQBS = 5;
1088
1089	// Should introspect to get the list of interpolators :-(
1090        // We can "dir" on VipsInterpolate to get a list of them, but we
1091        // can't get i18n'd descriptions until we have more
1092        // introspection stuff in nip2.
1093
1094	/* Table to map interpol numbers to descriptive strings
1095	 */
1096	descriptions = [
1097		_ "Nearest neighbour",
1098		_ "Bilinear",
1099		_ "Bicubic",
1100		_ "Upsize: reduced halo bicubic (LBB)",
1101		_ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)",
1102		_ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)"
1103	];
1104
1105	/* And to vips type names.
1106	 */
1107	types = [
1108		"VipsInterpolateNearest",
1109		"VipsInterpolateBilinear",
1110		"VipsInterpolateBicubic",
1111		"VipsInterpolateLbb",
1112		"VipsInterpolateNohalo",
1113		"VipsInterpolateVsqbs"
1114	];
1115}
1116
1117Interpolate type options = class {
1118	value = vips_object_new Interpolate_type.types?type [] options;
1119}
1120
1121Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR [];
1122
1123Interpolate_picker default = class
1124	Interpolate interp.value [] {
1125	_vislevel = 2;
1126
1127	interp = Option "Interpolation" Interpolate_type.descriptions default;
1128}
1129
1130Render_intent = class {
1131	PERCEPTUAL = 0;
1132	RELATIVE = 1;
1133	SATURATION = 2;
1134	ABSOLUTE = 3;
1135
1136	/* Table to get names <-> numbers.
1137	 */
1138	names = Enum [
1139		_ "Perceptual" => PERCEPTUAL,
1140		_ "Relative" => RELATIVE,
1141		_ "Saturation" => SATURATION,
1142		_ "Absolute" => ABSOLUTE
1143	];
1144}
1145
1146// abstract base class for toolkit menus
1147Menu = class {}
1148
1149// a "----" line in a menu
1150Menuseparator = class Menu {}
1151
1152// abstract base class for items in menus
1153Menuitem label tooltip = class Menu {}
1154
1155Menupullright label tooltip = class Menuitem label tooltip {}
1156
1157Menuaction label tooltip = class Menuitem label tooltip {}
1158
1159/* Plots.
1160 */
1161
1162Plot_style = class {
1163	POINT = 0;
1164	LINE = 1;
1165	SPLINE = 2;
1166	BAR = 3;
1167
1168	names = Enum [
1169		_ "Point" => POINT,
1170		_ "Line" => LINE,
1171		_ "Spline" => SPLINE,
1172		_ "Bar" => BAR
1173	];
1174}
1175
1176Plot_format = class {
1177	YYYY = 0;
1178	XYYY = 1;
1179	XYXY = 2;
1180
1181	names = Enum [
1182		_ "YYYY" => YYYY,
1183		_ "XYYY" => XYXY,
1184		_ "XYXY" => XYXY
1185	];
1186}
1187
1188Plot_type = class {
1189	/* Lots of Ys (ie. multiple line plots).
1190	 */
1191	YYYY = 0;
1192
1193	/* First column of matrix is X position, others are Ys (ie. multiple XY
1194	 * line plots, all with the same Xes).
1195	 */
1196	XYYY = 1;
1197
1198	/* Many independent XY plots.
1199	 */
1200	XYXY = 2;
1201}
1202
1203/* "options" is a list of ["key", value] pairs.
1204 */
1205Plot options value = class
1206	scope.Image value {
1207	Image value = this.Plot options value;
1208	to_image dpi = extract_bands 0 3
1209		(graph_export_image (to_real dpi) this);
1210}
1211
1212Plot_matrix options value = class
1213	Plot options (to_image value).value {
1214}
1215
1216Plot_histogram value = class
1217	scope.Plot [] value {
1218}
1219
1220Plot_xy value = class
1221	scope.Plot [$format => Plot_format.XYYY] value {
1222}
1223
1224/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate
1225 * empty slots, for example.
1226 */
1227NULL = class
1228	_Object {
1229	oo_binary_table op x = [
1230		// the only operation we allow is equality .. use pointer equality,
1231		// this lets us test a == NULL and a != NULL
1232		[this === x,
1233			op.type == Operator_type.RELATIONAL &&
1234			op.op_name == "equal"],
1235		[this !== x,
1236			op.type == Operator_type.RELATIONAL &&
1237			op.op_name == "not_equal"]
1238	] ++ super.oo_binary_table op x;
1239}
1240