1Image_new_item = class
2	Menupullright "_New" "make new things" {
3	Image_black_item = class
4		Menuaction "_Image" "make a new image" {
5		format_names = [
6			"8-bit unsigned int - UCHAR", 		// 0
7			"8-bit signed int - CHAR", 			// 1
8			"16-bit unsigned int - USHORT", 	// 2
9			"16-bit signed int - SHORT", 		// 3
10			"32-bit unsigned int - UINT", 		// 4
11			"32-bit signed int - INT", 			// 5
12			"32-bit float - FLOAT", 			// 6
13			"64-bit complex - COMPLEX", 		// 7
14			"64-bit float - DOUBLE", 			// 8
15			"128-bit complex - DPCOMPLEX" 		// 9
16		];
17
18		action = class
19			Image _result {
20			_vislevel = 3;
21
22			nwidth = Expression "Image width (pixels)" 64;
23			nheight = Expression "Image height (pixels)" 64;
24			nbands = Expression "Image bands" 1;
25			format_option = Option "Image format" format_names 0;
26			type_option = Option_enum
27				Image_type.type_names "Image type" "B_W";
28			pixel = Expression "Pixel value" 0;
29
30			_result
31				= image_new (to_real nwidth) (to_real nheight) (to_real nbands)
32					(to_real format_option) Image_coding.NOCODING
33					type_option.value_thing pixel.expr 0 0;
34		}
35	}
36
37	Image_new_from_image_item = class
38		Menuaction "_From Image" "make a new image based on image x" {
39		action x = class
40			Image _result {
41				_vislevel = 3;
42
43				pixel = Expression "Pixel value" 0;
44
45				_result
46					= image_new x.width x.height x.bands
47						x.format x.coding x.type pixel.expr x.xoffset x.yoffset;
48		}
49	}
50
51	Image_region_item = class
52		Menupullright "_Region on Image" "make a new region on an image" {
53		Region_item = class
54			Menuaction "_Region" "make a region on an image" {
55			action image = scope.Region_relative image 0.25 0.25 0.5 0.5;
56		}
57
58		Mark_item = class
59			Menuaction "_Point" "make a point on an image" {
60			action image = scope.Mark_relative image 0.5 0.5;
61		}
62
63		Arrow_item = class
64			Menuaction "_Arrow" "make an arrow on an image" {
65			action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5;
66		}
67
68		HGuide_item = class
69			Menuaction "_Horizontal Guide"
70				"make a horizontal guide on an image" {
71			action image = scope.HGuide image 0.5;
72		}
73
74		VGuide_item = class
75			Menuaction "_Vertical Guide" "make a vertical guide on an image" {
76			action image = scope.VGuide image 0.5;
77		}
78
79    	sep1 = Menuseparator;
80
81		Move_item = class
82			Menuaction "From Region"
83				"new region using existing region as a guide" {
84			action a b
85				= map_binary process a b
86			{
87				process a b
88					= x.Region target x.left x.top x.width x.height,
89							is_Region x
90					= x.Arrow target x.left x.top x.width x.height,
91							is_Arrow x
92					= error "bad arguments to region-from-region"
93				{
94					// prefer image then region
95					compare a b
96						= false,
97							!is_Image a && is_Image b
98						= false,
99							is_Region a && !is_Region b
100						= true;
101
102					args' = sortc compare [a, b];
103					target = args'?0;
104					x = args'?1;
105				}
106			}
107		}
108	}
109}
110
111Image_convert_to_image_item = class
112	Menuaction "Con_vert to Image" "convert anything to an image" {
113	action x = to_image x;
114}
115
116#separator
117
118Image_header_item = class
119Menupullright "_Header" "do stuff to the image header" {
120
121Image_get_item = class
122Menupullright "_Get" "get header fields" {
123
124// the header fields we can get
125fields = class {
126	type = 0;
127	width = 1;
128	height = 2;
129	format = 3;
130	bands = 4;
131	xres = 5;
132	yres = 6;
133	xoffset = 7;
134	yoffset = 8;
135	coding = 9;
136
137			field_names = Enum [
138				["width", width],
139				["height", height],
140				["bands", bands],
141				["format", format],
142				["type", type],
143				["xres", xres],
144				["yres", yres],
145				["xoffset", xoffset],
146				["yoffset", yoffset],
147				["coding", coding]
148			];
149
150			field_option name = Option_enum field_names (_ "Field") name;
151
152			field_funcs = Table [
153				[type, get_type],
154				[width, get_width],
155				[height, get_height],
156				[format, get_format],
157				[bands, get_bands],
158				[xres, get_xres],
159				[yres, get_yres],
160				[xoffset, get_xoffset],
161				[yoffset, get_yoffset],
162				[coding, get_coding]
163			];
164		}
165
166		get_field field_name x = class
167			_result {
168			_vislevel = 3;
169
170			field = fields.field_option field_name;
171
172			_result
173				= map_unary (Real @
174					fields.field_funcs.lookup 0 1 field.value_thing) x;
175		}
176
177		Width_item = class
178			Menuaction "_Width" "get width" {
179			action x = get_field "width" x;
180		}
181
182		Height_item = class
183			Menuaction "_Height" "get height" {
184			action x = get_field "height" x;
185		}
186
187		Bands_item = class
188			Menuaction "_Bands" "get bands" {
189			action x = get_field "bands" x;
190		}
191
192		Format_item = class
193			Menuaction "_Format" "get format" {
194			action x = get_field "format" x;
195		}
196
197		Type_item = class
198			Menuaction "_Type" "get type" {
199			action x = get_field "type" x;
200		}
201
202		Xres_item = class
203			Menuaction "_Xres" "get X resolution" {
204			action x = get_field "xres" x;
205		}
206
207		Yres_item = class
208			Menuaction "_Yres" "get Y resolution" {
209			action x = get_field "yres" x;
210		}
211
212		Xoffset_item = class
213			Menuaction "X_offset" "get X offset" {
214			action x = get_field "xoffset" x;
215		}
216
217		Yoffset_item = class
218			Menuaction "Yo_ffset" "get Y offset" {
219			action x = get_field "yoffset" x;
220		}
221
222		Coding_item = class
223			Menuaction "_Coding" "get coding" {
224			action x = get_field "coding" x;
225		}
226	}
227
228    sep1 = Menuseparator;
229
230	Image_set_meta_item = class
231		Menuaction "_Set" "set image metadata" {
232		action x = class
233			_result {
234			_vislevel = 3;
235
236			fname = String "Field" "field-name";
237			val = Expression "Value" 42;
238
239			_result
240				= map_unary process x
241			{
242				process image
243					= set_header fname.value val.expr image;
244			}
245		}
246	}
247
248	Image_edit_header_item = class
249		Menuaction "_Edit" "change advisory header fields of image" {
250		type_names = Image_type.type_names;
251		all_names = sort (map (extract 0) type_names.value);
252
253		get_prop has get def x
254			= get x, has x
255			= def;
256
257		action x = class
258			_result {
259			_vislevel = 3;
260
261			nxres = Expression "Xres" (get_prop has_xres get_xres 1 x);
262			nyres = Expression "Yres" (get_prop has_yres get_yres 1 x);
263			nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x);
264			nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x);
265
266			type_option
267				= Option_enum Image_type.type_names "Image type"
268					(Image_type.type_names.get_name type)
269			{
270				type
271					= x.type, is_Image x
272					= Image_type.MULTIBAND;
273			}
274
275			_result
276				= map_unary process x
277			{
278				process image
279					= Image (im_copy_set image.value type_option.value_thing
280						(to_real nxres) (to_real nyres)
281						(to_real nxoff) (to_real nyoff));
282			}
283		}
284	}
285}
286
287Image_number_format_item = class
288	Menupullright "Set _Format" "convert numeric format" {
289
290	U8_item = class
291		Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" {
292		action x = map_unary cast_unsigned_char x;
293	}
294
295	U16_item = class
296		Menuaction "1_6 bit unsigned"
297			"convert to unsigned 16 bit [0, 65535]" {
298		action x = map_unary cast_unsigned_short x;
299	}
300
301	U32_item = class
302		Menuaction "_32 bit unsigned"
303			"convert to unsigned 32 bit [0, 4294967295]" {
304		action x = map_unary cast_unsigned_int x;
305	}
306
307    sep1 = Menuseparator;
308
309	S8_item = class
310		Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" {
311		action x = map_unary cast_signed_char x;
312	}
313
314	S16_item = class
315		Menuaction "16 b_it signed"
316			"convert to signed 16 bit [-32768, 32767]" {
317		action x = map_unary cast_signed_short x;
318	}
319
320	S32_item = class
321		Menuaction "32 bi_t signed"
322			"convert to signed 32 bit [-2147483648, 2147483647]" {
323		action x = map_unary cast_signed_int x;
324	}
325
326    sep2 = Menuseparator;
327
328	Float_item = class
329		Menuaction "_Single precision float"
330			"convert to IEEE 32 bit float" {
331		action x = map_unary cast_float x;
332	}
333
334	Double_item = class
335		Menuaction "_Double precision float"
336			"convert to IEEE 64 bit float" {
337		action x = map_unary cast_double x;
338	}
339
340    sep3 = Menuseparator;
341
342	Scmplxitem = class
343		Menuaction "Single _precision complex"
344			"convert to 2 x IEEE 32 bit float" {
345		action x = map_unary cast_complex x;
346	}
347
348	Dcmplx_item = class
349		Menuaction "Double p_recision complex"
350			"convert to 2 x IEEE 64 bit float" {
351		action x = map_unary cast_double_complex x;
352	}
353}
354
355Image_levels_item = class
356	Menupullright "_Levels" "change image levels" {
357	Scale_item = class
358		Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" {
359		action x = map_unary scale x;
360	}
361
362	Linear_item = class
363		Menuaction "_Linear" "linear transform of image levels" {
364		action x = class
365			_result {
366			_vislevel = 3;
367
368			scale = Scale "Scale" 0.001 3 1;
369			offset = Scale "Offset" (-128) 128 0;
370
371			_result
372				= map_unary adj x
373			{
374				adj x
375					// only force back to input type if this is a thing
376					// with a type ... so we work for Colour / Matrix etc.
377					= clip2fmt x.format x', has_member "format" x
378					= x'
379				{
380					x' = x * scale + offset;
381				}
382			}
383		}
384	}
385
386	Gamma_item = class
387		Menuaction "_Power" "power transform of image levels (gamma)" {
388		action x = class
389			_result {
390			_vislevel = 3;
391
392			gamma = Scale "Gamma" 0.001 4 1;
393			image_maximum_hint = "You may need to change image_maximum if " ++
394				"this is not an 8 bit image";
395			im_mx
396				= Expression "Image maximum" mx
397			{
398				mx
399						= Image_format.maxval x.format, has_format x
400						= 255;
401			}
402
403			_result
404				= map_unary gam x
405			{
406				gam x
407					= clip2fmt (get_format x) x', has_format x
408					= x'
409				{
410					x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma;
411				}
412			}
413		}
414	}
415
416	Tone_item = class
417		Menuaction "_Tone Curve" "adjust tone curve" {
418		action x = class
419			_result {
420			_vislevel = 3;
421
422			b = Scale "Black point"  0 100 0;
423			w = Scale "White point"  0 100 100;
424
425			sp = Scale "Shadow point" 0.1 0.3 0.2;
426			mp = Scale "Mid-tone point" 0.4 0.6 0.5;
427			hp = Scale "Highlight point" 0.7 0.9 0.8;
428
429			sa = Scale "Shadow adjust" (-15) 15 0;
430			ma = Scale "Mid-tone adjust" (-30) 30 0;
431			ha = Scale "Highlight adjust" (-15) 15 0;
432
433			curve = tone_build x.format b w sp mp hp sa ma ha;
434
435			_result = map_unary (hist_map curve) x;
436		}
437	}
438}
439
440Image_transform_item = class
441	Menupullright "_Transform" "transform images" {
442	Rotate_item = class
443		Menupullright "Ro_tate" "rotate image" {
444		Fixed_item = class
445			Menupullright "_Fixed" "clockwise rotation by fixed angles" {
446	        rotate_widget default x = class
447				_result {
448				_vislevel = 3;
449
450				angle = Option "Rotate by" [
451					"Don't rotate",
452					"90 degrees clockwise",
453					"180 degrees",
454					"90 degrees anticlockwise"
455				] default;
456
457				_result
458					= map_unary process x
459				{
460					process in = [
461						in,
462						rot90 in,
463						rot180 in,
464						rot270 in
465					] ? angle;
466				}
467			}
468
469			Rot90_item = class
470				Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" {
471				action x = rotate_widget 1 x;
472			}
473
474			Rot180_item = class
475				Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" {
476				action x = rotate_widget 2 x;
477			}
478
479			Rot270_item = class
480				Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" {
481				action x = rotate_widget 3 x;
482			}
483		}
484
485		Free_item = class
486			Menuaction "_Free" "clockwise rotation by any angle" {
487			action x = class
488				_result {
489				_vislevel = 3;
490
491				angle = Scale "Angle" (-180) 180 0;
492
493				_result
494					= map_unary process x
495				{
496					process image
497						= rotate angle image;
498				}
499			}
500		}
501
502		Straighten_item = class
503			Menuaction "_Straighten"
504				("smallest rotation that makes an arrow either horizontal " ++
505				"or vertical") {
506			action x
507				= map_unary straighten x
508			{
509				straighten arrow
510					= rotate angle'' arrow.image
511				{
512					x = arrow.width;
513					y = arrow.height;
514
515					angle = im (polar (x, y));
516
517					angle'
518						= angle - 360, angle > 315
519						= angle - 180, angle > 135
520						= angle;
521
522					angle''
523						= -angle', angle' >= (-45) && angle' < 45
524						= 90 - angle';
525				}
526			}
527		}
528	}
529
530	Flip_item = class
531		Menupullright "_Flip" "mirror left/right or up/down" {
532		Left_right_item = class
533			Menuaction "_Left Right" "mirror object left/right" {
534			action x = map_unary fliplr x;
535		}
536
537		Top_bottom_item = class
538			Menuaction "_Top Bottom" "mirror object top/bottom" {
539			action x = map_unary fliptb x;
540		}
541	}
542
543	Resize_item = class
544		Menupullright "_Resize" "change image size" {
545		_interp = Option_enum Interpolate.names "Interpolation" "Bilinear";
546
547		Scale_item = class
548			Menuaction "_Scale" "scale image size by a factor" {
549			action x = class
550				_result {
551				_vislevel = 3;
552
553				xfactor = Expression "Horizontal scale factor" 1;
554				yfactor = Expression "Vertical scale factor" 1;
555				interp = _interp;
556
557				_result
558					= map_unary process x
559				{
560					process image
561						= resize xfactor yfactor interp.value_thing image;
562				}
563			}
564		}
565
566		Size_item = class
567			Menuaction "_Size To" "resize to a fixed size" {
568			action x = class
569				_result {
570				_vislevel = 3;
571
572				which = Option "Resize axis" [
573					"Shortest",
574					"Longest",
575					"Horizontal",
576					"Vertical"
577				] 0;
578				size = Expression "Resize to (pixels)" 128;
579				interp = _interp;
580
581				_result
582					= map_unary process x
583				{
584					process image
585						= resize fac fac interp.value_thing image
586					{
587						xfac = to_real size / image.width;
588						yfac = to_real size / image.height;
589						max_factor = max_pair xfac yfac;
590						min_factor = min_pair xfac yfac;
591						fac = [max_factor, min_factor, xfac, yfac]?which;
592					}
593				}
594			}
595		}
596
597		Resize_canvas_item = class
598			Menuaction "_Canvas" "change size of surrounding image" {
599			action x = class
600				_result {
601				_vislevel = 3;
602
603				// try to guess a sensible size for the new image
604				_guess_size
605					= x.rect, is_Image x
606					= Rect 0 0 100 100;
607
608				nwidth = Expression "New width (pixels)" _guess_size.width;
609				nheight = Expression "New height (pixels)" _guess_size.height;
610				bgcolour = Expression "Background colour" 0;
611
612				position = Option "Position" [
613					"North-west",
614					"North",
615					"North-east",
616					"West",
617					"Centre",
618					"East",
619					"South-west",
620					"South",
621					"South-east",
622					"Specify in pixels"
623				] 4;
624				left = Expression "Pixels from left" 0;
625				top = Expression "Pixels from top" 0;
626
627				_result
628					= map_unary process x
629				{
630					process image
631						= insert_noexpand xp yp image background
632					{
633						width = image.width;
634						height = image.height;
635						coding = image.coding;
636						bands
637							= 3, coding == Image_coding.LABPACK
638							= image.bands;
639						format
640							= Image_format.FLOAT, coding == Image_coding.LABPACK
641							= image.format;
642						type = image.type;
643
644						// placement vectors ... left, centre, right
645						xposv = [0, to_real nwidth / 2 - width / 2,
646							to_real nwidth - width];
647						yposv = [0, to_real nheight / 2 - height / 2,
648							to_real nheight - height];
649						xp
650							= left, position == 9
651							= xposv?((int) (position % 3));
652						yp
653							= top, position == 9
654							= yposv?((int) (position / 3));
655
656						background = image_new nwidth nheight
657							bands format coding type bgcolour.expr 0 0;
658					}
659				}
660			}
661		}
662	}
663
664	Image_perspective_item = Perspective_item;
665
666	Image_rubber_item = class
667		Menupullright "Ru_bber Sheet"
668			"automatically warp images to superposition" {
669		rubber_interp = Option "Interpolation"
670			(map (extract 0) Interpolate.names.value) Interpolate.BILINEAR;
671		rubber_order = Option "Order" ["0", "1", "2", "3"] 1;
672		rubber_wrap = Toggle "Wrap image edges" false;
673
674		// a transform ... a matrix, plus the size of the image the
675		// matrix was made for
676		Transform matrix image_width image_height = class
677			matrix {
678			// scale a transform ... if it worked for a m by n image, make
679			// it work for a (m * xfac) by (y * yfac) image
680			rescale xfac yfac
681				= Transform (Matrix (map2 (map2 multiply) matrix.value facs))
682					(image_width * xfac) (image_height * yfac)
683			{
684				facs = [
685					[xfac, yfac],
686					[1, 1],
687					[1, 1],
688					[1 / xfac, 1 / yfac],
689					[1 / xfac, 1 / yfac],
690					[1 / xfac, 1 / yfac]
691				];
692			}
693		}
694
695		// yuk!!!! fix is_instanceof to not need absolute names
696		is_Transform = is_instanceof
697			"Image_transform_item.Image_rubber_item.Transform";
698
699		Find_item = class
700			Menuaction "_Find"
701				("find a transform which will map sample image onto " ++
702				"reference") {
703			action reference sample = class
704				_transform {
705				_vislevel = 3;
706
707				// controls
708				order = rubber_order;
709				interp = rubber_interp;
710				wrap = rubber_wrap;
711				max_err = Expression "Maximum error" 0.3;
712				max_iter = Expression "Maximum iterations" 10;
713
714				// transform
715				_result = transform_search max_err max_iter order interp wrap
716						sample reference;
717
718				transformed_image = Image _result?0;
719				_transform = Transform _result?1
720					reference.width reference.height;
721				final_error = _result?2;
722			}
723		}
724
725		Apply_item = class
726			Menuaction "_Apply" "apply a transform to an image" {
727			action a b = class
728				_result {
729				_vislevel = 3;
730
731				// controls
732				interp = rubber_interp;
733				wrap = rubber_wrap;
734
735				_result
736					= map_binary trans a b
737				{
738					trans a b
739						= transform interp wrap t' i
740					{
741						// get the transform arg first
742						args = sortc (const is_Transform) [a, b];
743						i = args?0;
744						t = args?1;
745						t' = t.rescale (i.width / t.image_width)
746							(i.height / t.image_height);
747					}
748				}
749			}
750		}
751	}
752
753    sep1 = Menuseparator;
754
755	Match_item = class
756		Menuaction "_Linear Match"
757			"rotate and scale one image to match another" {
758		action x y = class
759			_result {
760			_vislevel = 3;
761
762			// try to find an image ... for a group, get the first item
763			find_image x
764				= x, is_Image x
765				= find_image x?0, is_list x
766				= find_image x.value, is_class x && has_value x
767				= error "unable to find image";
768
769			_a = find_image x;
770			_b = find_image y;
771
772			ap1 = Mark_relative _a 0.5 0.25;
773			bp1 = Mark_relative _b 0.5 0.25;
774			ap2 = Mark_relative _a 0.5 0.75;
775			bp2 = Mark_relative _b 0.5 0.75;
776
777			refine = Toggle "Refine selected tie-points" false;
778			lock = Toggle "No resize" false;
779
780			_result
781				= map_binary process x y
782			{
783				process a b
784					= Image b'''
785				{
786					_prefs = Workspaces.Preferences;
787					window = _prefs.MOSAIC_WINDOW_SIZE;
788					object = _prefs.MOSAIC_OBJECT_SIZE;
789
790					a' = a.value;
791					b' = b.value;
792
793					b'' = clip2fmt a.format b';
794
795					// return p2 ... if lock is set, return a p2 a standard
796					// distance along the vector joining p1 and p2
797					norm p1 p2
798						= Rect left' top' 0 0, lock
799						= p2
800					{
801						v = (p2.left - p1.left, p2.top - p1.top);
802						// 100000 to give precision since we pass points as
803						// ints to match
804						n = 100000 * sign v;
805						left' = p1.left + re n;
806						top' = p1.top + im n;
807					}
808
809					ap2'' = norm ap1 ap2;
810					bp2'' = norm bp1 bp2;
811
812					b'''
813						= im_match_linear_search a' b''
814							ap1.left ap1.top bp1.left bp1.top
815							ap2''.left ap2''.top bp2''.left bp2''.top
816							object window,
817								// we can't search if lock is on
818								refine && !lock
819						= im_match_linear a' b''
820							ap1.left ap1.top bp1.left bp1.top
821							ap2''.left ap2''.top bp2''.left bp2''.top;
822				}
823			}
824		}
825	}
826
827	Image_perspective_match_item = Perspective_match_item;
828}
829
830Image_band_item = class
831	Menupullright "_Band" "manipulate image bands" {
832	// like extract_bands, but return [] for zero band image
833	// makes compose a bit simpler
834	exb b n x
835		= [], to_real n == 0
836		= extract_bands b n x;
837
838	Extract_item = class Menuaction "_Extract" "extract bands from image" {
839		action x = class
840			_result {
841			_vislevel = 3;
842
843			first = Expression "Extract from band" 0;
844			number = Expression "Extract this many bands" 1;
845
846			_result = map_unary (exb first number) x;
847		}
848	}
849
850	Insert_item = class Menuaction "_Insert" "insert bands into image" {
851		action x y = class
852			_result {
853			_vislevel = 3;
854
855			first = Expression "Insert at position" 0;
856
857			_result
858				= map_binary process x y
859			{
860				process im1 im2
861					= exb 0 f im1 ++ im2 ++ exb f (b - f) im1
862				{
863					f = to_real first;
864					b = im1.bands;
865				}
866			}
867		}
868	}
869
870	Delete_item = class Menuaction "_Delete" "delete bands from image" {
871		action x = class
872			_result {
873			_vislevel = 3;
874
875			first = Expression "Delete from band" 0;
876			number = Expression "Delete this many bands" 1;
877
878			_result
879				= map_unary process x
880			{
881				process im
882					= exb 0 f im ++ exb (f + n) (b - (f + n)) im
883				{
884					f = to_real first;
885					n = to_real number;
886					b = im.bands;
887				}
888			}
889		}
890	}
891
892    sep1 = Menuseparator;
893
894	To_dimension_item = class
895		Menuaction "To D_imension" "convert bands to width or height" {
896		action x = class
897			_result {
898			_vislevel = 3;
899
900			orientation = Option "Orientation" [
901				"Horizontal",
902				"Vertical"
903			] 0;
904
905			_result
906				= map_unary process x
907			{
908				process im
909					= foldr1 [join_lr, join_tb]?orientation (bandsplit im);
910			}
911		}
912	}
913
914	To_bands_item = class
915		Menuaction "To B_ands" "turn width or height to bands" {
916		action x = class
917			_result {
918			_vislevel = 3;
919
920			orientation = Option "Orientation" [
921				"Horizontal",
922				"Vertical"
923			] 0;
924
925			_result
926				= map_unary process x
927			{
928				process im
929					= bandjoin (map extract_column [0 .. im.width - 1]),
930						orientation == 0
931					= bandjoin (map extract_row [0 .. im.height - 1])
932				{
933					extract_column n
934						= extract_area n 0 1 im.height im;
935					extract_row n
936						= extract_area 0 n im.width 1 im;
937				}
938			}
939		}
940	}
941}
942
943Image_crop_item = class
944	Menuaction "_Crop" "extract a rectangular area from an image" {
945	action x = class
946		_result {
947		_vislevel = 3;
948
949		// try to find the image geometry ... don't bother trying to look
950		// inside groups though
951		_geo
952			= x.rect, is_Image x
953			= Rect 0 0 100 100;
954
955		l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4));
956		t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4));
957		w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2)));
958		h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2)));
959
960		_result
961			= map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr]
962		{
963			extract im l t w h
964				= extract_area left' top' width' height' im
965			{
966				width' = min_pair (to_real w) im.width;
967				height' = min_pair (to_real h) im.height;
968				left' = range 0 (to_real l) (im.width - width');
969				top' = range 0 (to_real t) (im.height - height');
970			}
971		}
972	}
973}
974
975Image_insert_item = class
976	Menuaction "_Insert" "insert a small image into a large image" {
977	action a b
978		= insert_position, is_Group a || is_Group b
979		= insert_area
980	{
981		insert_area = class
982			_result {
983			_check_args = [
984				[a, "a", check_Image],
985				[b, "b", check_Image]
986			];
987			_check_all = [
988				[a.coding == b.coding && a.bands == b.bands,
989					"a.coding == b.coding && a.bands == b.bands"]
990			];
991			_vislevel = 3;
992
993			// sort to get smallest first
994			_pred x y = x.width * x.height < y.width * y.height;
995			_sorted = sortc _pred [a, b];
996			_a' = _sorted?0;
997			_b' = _sorted?1;
998
999			place
1000				= Area _b' left top width height
1001			{
1002				// be careful in case b is smaller than a
1003				left = max_pair 0 ((_b'.width - _a'.width) / 2);
1004				top = max_pair 0 ((_b'.height - _a'.height) / 2);
1005				width = min_pair _a'.width _b'.width;
1006				height = min_pair _a'.height _b'.height;
1007			}
1008
1009			_result
1010				= insert_noexpand place.left place.top
1011					(clip2fmt _b'.format a'') _b'
1012			{
1013				a'' = extract_area 0 0 place.width place.height _a';
1014			}
1015		}
1016
1017		insert_position = class
1018			_result {
1019			_vislevel = 3;
1020
1021			position = Option "Position" [
1022				"North-west",
1023				"North",
1024				"North-east",
1025				"West",
1026				"Centre",
1027				"East",
1028				"South-west",
1029				"South",
1030				"South-east",
1031				"Specify in pixels"
1032			] 4;
1033			left = Expression "Pixels from left" 0;
1034			top = Expression "Pixels from top" 0;
1035
1036			_result
1037				= map_binary insert a b
1038			{
1039				insert a b
1040					= insert_noexpand left top (clip2fmt b.format a) b,
1041						position == 9
1042					= insert_noexpand xp yp (clip2fmt b.format a) b
1043				{
1044					xr = b.width - a.width;
1045					yr = b.height - a.height;
1046					xp = [0, xr / 2, xr]?((int) (position % 3));
1047					yp = [0, yr / 2, yr]?((int) (position / 3));
1048				}
1049			}
1050		}
1051	}
1052}
1053
1054Image_select_item = Select_item;
1055
1056Image_join_item = class
1057	Menupullright "_Join" "join two or more images together" {
1058	Bandwise_item = class
1059		Menuaction "_Bandwise" "join two images bandwise" {
1060		action a b = join a b;
1061	}
1062
1063    sep1 = Menuseparator;
1064
1065	join_lr shim bg align a b
1066		= im2
1067	{
1068		w = a.width + b.width + shim;
1069		h = max_pair a.height b.height;
1070
1071		back = image_new w h a.bands a.format a.coding a.type bg 0 0;
1072
1073		ya = [0, max_pair 0 ((b.height - a.height)/2),
1074			max_pair 0 (b.height - a.height)];
1075		yb = [0, max_pair 0 ((a.height - b.height)/2),
1076			max_pair 0 (a.height - b.height)];
1077
1078		im1 = insert_noexpand 0 ya?align a back;
1079		im2 = insert_noexpand (a.width + shim) yb?align b im1;
1080	}
1081
1082	join_tb shim bg align a b
1083		= im2
1084	{
1085		w = max_pair a.width b.width;
1086		h = a.height + b.height + shim;
1087
1088		back = image_new w h a.bands a.format a.coding a.type bg 0 0;
1089
1090		xa = [0, max_pair 0 ((b.width - a.width)/2),
1091			max_pair 0 (b.width - a.width)];
1092		xb = [0, max_pair 0 ((a.width - b.width)/2),
1093			max_pair 0 (a.width - b.width)];
1094
1095		im1 = insert_noexpand xa?align 0 a back;
1096		im2 = insert_noexpand xb?align (a.height + shim) b im1;
1097	}
1098
1099	halign_names = ["Top", "Centre", "Bottom"];
1100	valign_names = ["Left", "Centre", "Right"];
1101
1102	Left_right_item = class
1103		Menuaction "_Left to Right" "join two images left-right" {
1104		action a b = class
1105			_result {
1106			_vislevel = 3;
1107
1108			shim = Scale "Spacing" 0 100 0;
1109			bg_colour = Expression "Background colour" 0;
1110			align = Option "Alignment" halign_names 1;
1111
1112			_result = map_binary
1113				(join_lr shim.value bg_colour.expr align.value) a b;
1114		}
1115	}
1116
1117	Top_bottom_item = class
1118		Menuaction "_Top to Bottom" "join two images top-bottom" {
1119		action a b = class
1120			_result {
1121			_vislevel = 3;
1122
1123			shim = Scale "Spacing" 0 100 0;
1124			bg_colour = Expression "Background colour" 0;
1125			align = Option "Alignment" valign_names 1;
1126
1127			_result = map_binary
1128				(join_tb shim.value bg_colour.expr align.value) a b;
1129		}
1130	}
1131
1132    sep2 = Menuseparator;
1133
1134	Array_item = class
1135		Menuaction "_Array"
1136			"join a list of lists of images into a single image" {
1137		action x = class
1138			_result {
1139			_vislevel = 3;
1140
1141			hshim = Scale "Horizontal spacing" (-100) (100) 0;
1142			vshim = Scale "Vertical spacing" (-100) (100) 0;
1143			bg_colour = Expression "Background colour" 0;
1144			halign = Option "Horizontal alignment" valign_names 1;
1145			valign = Option "Vertical alignment" halign_names 1;
1146
1147			_result
1148				= (image_set_origin 0 0 @
1149					foldl1 (join_tb vshim.value bg_colour.expr halign.value) @
1150					map (foldl1 (join_lr hshim.value
1151						bg_colour.expr valign.value))) (to_list (to_list x));
1152		}
1153	}
1154}
1155
1156Image_tile_item = class
1157	Menupullright "Til_e" "tile an image across and down" {
1158	tile_widget default_type x = class
1159		_result {
1160		_vislevel = 3;
1161
1162		across = Expression "Tiles across" 2;
1163		down = Expression "Tiles down" 2;
1164		repeat = Option "Tile type"
1165			["Replicate", "Four-way mirror"] default_type;
1166
1167		_result
1168			= map_unary process x
1169		{
1170			process image
1171				= tile across down image, repeat == 0
1172				= tile across down image''
1173			{
1174				image' = insert image.width 0 (fliplr image) image;
1175				image'' = insert 0 image.height (fliptb image') image';
1176			}
1177		}
1178	}
1179
1180	Replicate_item = class
1181		Menuaction "_Replicate" "replicate image across and down" {
1182		action x = tile_widget 0 x;
1183	}
1184
1185	Fourway_item = class
1186		Menuaction "_Four-way Mirror" "four-way mirror across and down" {
1187		action x = tile_widget 1 x;
1188	}
1189
1190	Chop_item = class
1191		Menuaction "_Chop Into Tiles" "slice an image into tiles" {
1192		action x = class
1193			_result {
1194			_vislevel = 3;
1195
1196			tile_width = Expression "Tile width" 100;
1197			tile_height = Expression "Tile height" 100;
1198			hoverlap = Expression "Horizontal overlap" 0;
1199			voverlap = Expression "Vertical overlap" 0;
1200
1201			_result
1202				= map_unary (Group @ map Group @ process) x
1203			{
1204				process x
1205					= imagearray_chop tile_width tile_height
1206						hoverlap voverlap x;
1207			}
1208		}
1209	}
1210}
1211
1212#separator
1213
1214Pattern_images_item = class
1215	Menupullright "_Patterns" "make a variety of useful patterns" {
1216	Grey_item = class
1217		Menuaction "Grey _Ramp" "make a smooth grey ramp" {
1218		action = class
1219			_result {
1220			_vislevel = 3;
1221
1222			nwidth = Expression "Image width (pixels)" 64;
1223			nheight = Expression "Image height (pixels)" 64;
1224			orientation = Option "Orientation" [
1225				"Horizontal",
1226				"Vertical"
1227			] 0;
1228			foption = Option "Format" ["8 bit", "float"] 0;
1229
1230			_result
1231				= Image im
1232			{
1233				gen
1234					= im_grey, foption == 0
1235					= im_fgrey;
1236				w = to_real nwidth;
1237				h = to_real nheight;
1238				im
1239					= gen w h, orientation == 0
1240					= rot90 (gen h w);
1241			}
1242		}
1243	}
1244
1245	Xy_item = class
1246		Menuaction "_XY Image"
1247			"make a two band image whose pixel values are their coordinates" {
1248		action = class
1249			_result {
1250			_vislevel = 3;
1251
1252			nwidth = Expression "Image width (pixels)" 64;
1253			nheight = Expression "Image height (pixels)" 64;
1254
1255			_result = Image (make_xy nwidth nheight);
1256		}
1257	}
1258
1259	Gaussian_item = class
1260		Menuaction "Gaussian _Noise" "make an image of gaussian noise" {
1261		action = class
1262			_result {
1263			_vislevel = 3;
1264
1265			nwidth = Expression "Image width (pixels)" 64;
1266			nheight = Expression "Image height (pixels)" 64;
1267			mean = Scale "Mean" 0 255 128;
1268			deviation = Scale "Deviation" 0 128 50;
1269
1270			_result = Image (im_gaussnoise (to_real nwidth) (to_real nheight)
1271				mean.value deviation.value);
1272		}
1273	}
1274
1275	Fractal_item = class
1276		Menuaction "_Fractal" "make a fractal image" {
1277		action = class
1278			_result {
1279			_vislevel = 3;
1280
1281			nsize = Expression "Image size (pixels)" 64;
1282			dimension = Scale "Dimension" 2.001 2.999 2.001;
1283
1284			_result = Image (im_fractsurf (to_real nsize) dimension.value);
1285		}
1286	}
1287
1288	Checkerboard_item = class
1289		Menuaction "_Checkerboard" "make a checkerboard image" {
1290		action = class
1291			_result {
1292			_vislevel = 3;
1293
1294			nwidth = Expression "Image width (pixels)" 64;
1295			nheight = Expression "Image height (pixels)" 64;
1296			hpsize = Expression "Horizontal patch size" 8;
1297			vpsize = Expression "Vertical patch size" 8;
1298			hpoffset = Expression "Horizontal patch offset" 0;
1299			vpoffset = Expression "Vertical patch offset" 0;
1300
1301			_result
1302				= Image (xstripes ^ ystripes)
1303			{
1304				pixels = make_xy nwidth nheight;
1305				xpixels = pixels?0 + to_real hpoffset;
1306				ypixels = pixels?1 + to_real vpoffset;
1307
1308				make_stripe pix swidth = pix % (swidth * 2) >= swidth;
1309
1310				xstripes = make_stripe xpixels (to_real hpsize);
1311				ystripes = make_stripe ypixels (to_real vpsize);
1312			}
1313		}
1314	}
1315
1316	Grid_item = class
1317		Menuaction "Gri_d" "make a grid" {
1318		action = class
1319			_result {
1320			_vislevel = 3;
1321
1322			nwidth = Expression "Image width (pixels)" 64;
1323			nheight = Expression "Image height (pixels)" 64;
1324			hspace = Expression "Horizontal line spacing" 8;
1325			vspace = Expression "Vertical line spacing" 8;
1326			thick = Expression "Line thickness" 1;
1327			hoff = Expression "Horizontal grid offset" 4;
1328			voff = Expression "Vertical grid offset" 4;
1329
1330			_result
1331				= Image (xstripes | ystripes)
1332			{
1333				pixels = make_xy nwidth nheight;
1334				xpixels = pixels?0 + to_real hoff;
1335				ypixels = pixels?1 + to_real voff;
1336
1337				make_stripe pix swidth = pix % swidth < to_real thick;
1338
1339				xstripes = make_stripe xpixels (to_real hspace);
1340				ystripes = make_stripe ypixels (to_real vspace);
1341			}
1342		}
1343	}
1344
1345	Text_item = class
1346		Menuaction "_Text" "make a bitmap of some text" {
1347		action = class
1348			_result {
1349			_vislevel = 3;
1350
1351			text = String "Text to paint" "<i>Hello</i> world!";
1352			font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT;
1353			wrap = Expression "Wrap text at" 500;
1354			align = Option "Alignment" [
1355				"Left",
1356				"Centre",
1357				"Right"
1358			] 0;
1359			dpi = Expression "DPI" 300;
1360
1361			_result = Image (im_text text.value font.value
1362				(to_real wrap) align.value (to_real dpi));
1363		}
1364	}
1365
1366	New_CIELAB_slice_item = class
1367		Menuaction "CIELAB _Slice" "make a slice through CIELAB space" {
1368		action = class
1369			_result {
1370			_vislevel = 3;
1371
1372			nsize = Expression "Image size (pixels)" 64;
1373			L = Scale "L value" 0 100 50;
1374
1375			_result = Image (lab_slice (to_real nsize) L.value);
1376		}
1377	}
1378
1379	sense_option = Option "Sense" [
1380		"Pass",
1381		"Reject"
1382	] 0;
1383
1384	build fn size
1385		= (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn)
1386			(im_create_fmask size size);
1387
1388	New_ideal_item = class
1389		Menupullright "_Ideal Fourier Mask"
1390			"make various ideal Fourier filter masks" {
1391		High_low_item = class
1392			Menuaction "_High or Low Pass"
1393				("make a mask image for a highpass/lowpass " ++
1394					"ideal Fourier filter") {
1395			action = class
1396				_result {
1397				_vislevel = 3;
1398
1399				nsize = Expression "Image size (pixels)" 64;
1400				sense = sense_option;
1401				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1402
1403				_result
1404					= build param (to_real nsize)
1405				{
1406						param f = f sense.value fc.value 0 0 0 0;
1407				}
1408			}
1409		}
1410
1411		Ring_item = class
1412			Menuaction "_Ring Pass or Ring Reject"
1413				("make a mask image for an ring pass/reject " ++
1414				"ideal Fourier filter") {
1415			action = class
1416				_result {
1417				_vislevel = 3;
1418
1419				nsize = Expression "Image size (pixels)" 64;
1420				sense = sense_option;
1421				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1422				rw = Scale "Ring width" 0.01 0.99 0.5;
1423
1424				_result
1425					= build param (to_real nsize)
1426				{
1427						param f = f (sense.value + 6) fc.value rw.value 0 0 0;
1428				}
1429			}
1430		}
1431
1432		Band_item = class
1433			Menuaction "_Band Pass or Band Reject"
1434				("make a mask image for a band pass/reject " ++
1435				"ideal Fourier filter") {
1436			action = class
1437				_result {
1438				_vislevel = 3;
1439
1440				nsize = Expression "Image size (pixels)" 64;
1441				sense = sense_option;
1442				fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5;
1443				fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5;
1444				r = Scale "Radius" 0.01 0.99 0.5;
1445
1446				_result
1447					= build param (to_real nsize)
1448				{
1449						param f = f (sense.value + 12) fcx.value fcy.value
1450							r.value 0 0;
1451				}
1452			}
1453		}
1454	}
1455
1456	New_gaussian_item = class
1457		Menupullright "_Gaussian Fourier Mask"
1458			"make various Gaussian Fourier filter masks" {
1459		High_low_item = class
1460			Menuaction "_High or Low Pass"
1461				("make a mask image for a highpass/lowpass " ++
1462					"Gaussian Fourier filter") {
1463			action = class
1464				_result {
1465				_vislevel = 3;
1466
1467				nsize = Expression "Image size (pixels)" 64;
1468				sense = sense_option;
1469				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1470				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1471
1472				_result
1473					= build param (to_real nsize)
1474				{
1475						param f = f (sense.value + 4) fc.value ac.value 0 0 0;
1476				}
1477			}
1478		}
1479
1480		Ring_item = class
1481			Menuaction "_Ring Pass or Ring Reject"
1482				("make a mask image for an ring pass/reject " ++
1483				"Gaussian Fourier filter") {
1484			action = class
1485				_result {
1486				_vislevel = 3;
1487
1488				nsize = Expression "Image size (pixels)" 64;
1489				sense = sense_option;
1490				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1491				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1492				rw = Scale "Ring width" 0.01 0.99 0.5;
1493
1494				_result
1495					= build param (to_real nsize)
1496				{
1497						param f = f (sense.value + 10) fc.value rw.value
1498							ac.value 0 0;
1499				}
1500			}
1501		}
1502
1503		Band_item = class
1504			Menuaction "_Band Pass or Band Reject"
1505				("make a mask image for a band pass/reject " ++
1506				"Gaussian Fourier filter") {
1507			action = class
1508				_result {
1509				_vislevel = 3;
1510
1511				nsize = Expression "Image size (pixels)" 64;
1512				sense = sense_option;
1513				fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5;
1514				fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5;
1515				r = Scale "Radius" 0.01 0.99 0.5;
1516				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1517
1518				_result
1519					= build param (to_real nsize)
1520				{
1521						param f = f (sense.value + 16) fcx.value fcy.value
1522							r.value ac.value 0;
1523				}
1524			}
1525		}
1526	}
1527
1528	New_butterworth_item = class
1529		Menupullright "_Butterworth Fourier Mask"
1530			"make various Butterworth Fourier filter masks" {
1531		High_low_item = class
1532			Menuaction "_High or Low Pass"
1533				("make a mask image for a highpass/lowpass " ++
1534					"Butterworth Fourier filter") {
1535			action = class
1536				_result {
1537				_vislevel = 3;
1538
1539				nsize = Expression "Image size (pixels)" 64;
1540				sense = sense_option;
1541				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1542				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1543				order = Scale "Order" 1 10 2;
1544
1545				_result
1546					= build param (to_real nsize)
1547				{
1548						param f = f (sense.value + 2) order.value fc.value
1549							ac.value 0 0;
1550				}
1551			}
1552		}
1553
1554		Ring_item = class
1555			Menuaction "_Ring Pass or Ring Reject"
1556				("make a mask image for an ring pass/reject " ++
1557				"Butterworth Fourier filter") {
1558			action = class
1559				_result {
1560				_vislevel = 3;
1561
1562				nsize = Expression "Image size (pixels)" 64;
1563				sense = sense_option;
1564				fc = Scale "Frequency cutoff" 0.01 0.99 0.5;
1565				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1566				rw = Scale "Ring width" 0.01 0.99 0.5;
1567				order = Scale "Order" 1 10 2;
1568
1569				_result
1570					= build param (to_real nsize)
1571				{
1572						param f = f (sense.value + 8) order.value fc.value
1573							rw.value ac.value 0;
1574				}
1575			}
1576		}
1577
1578		Band_item = class
1579			Menuaction "_Band Pass or Band Reject"
1580				("make a mask image for a band pass/reject " ++
1581				"Butterworth Fourier filter") {
1582			action = class
1583				_result {
1584				_vislevel = 3;
1585
1586				nsize = Expression "Image size (pixels)" 64;
1587				sense = sense_option;
1588				fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5;
1589				fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5;
1590				r = Scale "Radius" 0.01 0.99 0.5;
1591				ac = Scale "Amplitude cutoff" 0.01 0.99 0.5;
1592				order = Scale "Order" 1 10 2;
1593
1594				_result
1595					= build param (to_real nsize)
1596				{
1597						param f = f (sense.value + 14) order.value fcx.value
1598							fcy.value r.value ac.value;
1599				}
1600			}
1601		}
1602	}
1603}
1604
1605Test_images_item = class
1606	Menupullright "Test I_mages" "make a variety of test images" {
1607	Eye_item = class
1608		Menuaction "_Spatial Response"
1609			"image for testing the eye's spatial response" {
1610		action = class
1611			_result {
1612			_vislevel = 3;
1613
1614			nwidth = Expression "Image width (pixels)" 64;
1615			nheight = Expression "Image height (pixels)" 64;
1616			factor = Scale "Factor" 0.001 1 0.2;
1617
1618			_result = Image (im_eye (to_real nwidth) (to_real nheight)
1619				factor.value);
1620		}
1621	}
1622
1623	Zone_plate = class
1624		Menuaction "_Zone Plate" "make a zone plate" {
1625		action = class
1626			_result {
1627			_vislevel = 3;
1628
1629			nsize = Expression "Image size (pixels)" 64;
1630
1631			_result = Image (im_zone (to_real nsize));
1632		}
1633	}
1634
1635	Frequency_test_chart_item = class
1636		Menuaction "_Frequency Testchart"
1637			"make a black/white frequency test pattern" {
1638		action = class
1639			_result {
1640			_vislevel = 3;
1641
1642			nwidth = Expression "Image width (pixels)" 64;
1643			sheight = Expression "Strip height (pixels)" 10;
1644			waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2];
1645
1646			_result
1647				= imagearray_assemble 0 0 (transpose [strips])
1648			{
1649				freq_slice wave = Image (sin (grey / wave) > 0);
1650				strips = map freq_slice waves.expr;
1651				grey = im_fgrey (to_real nwidth) (to_real sheight) *
1652					360 * (to_real nwidth);
1653			}
1654		}
1655	}
1656
1657	CRT_test_chart_item = class
1658		Menuaction "CRT _Phosphor Chart"
1659			"make an image for measuring phosphor colours" {
1660		action = class
1661			_result {
1662			_vislevel = 3;
1663
1664			brightness = Scale "Brightness" 0 255 200;
1665			psize = Expression "Patch size (pixels)" 32;
1666
1667			_result
1668				= Image (imagearray_assemble 0 0 [[green, red], [blue, white]])
1669			{
1670
1671				black = image_new (to_real psize) (to_real psize) 1
1672					Image_format.FLOAT Image_coding.NOCODING
1673					Image_type.B_W 0 0 0;
1674				notblack = black + brightness;
1675
1676				green = black ++ notblack ++ black;
1677				red = notblack ++ black ++ black;
1678				blue = black ++ black ++ notblack;
1679				white = notblack ++ notblack ++ notblack;
1680			}
1681		}
1682	}
1683
1684	Greyscale_chart_item = class
1685		Menuaction "_Greyscale" "make a greyscale" {
1686		action = class
1687			_result {
1688			_vislevel = 3;
1689
1690			pwidth = Expression "Patch width" 8;
1691			pheight = Expression "Patch height" 8;
1692			npatches = Expression "Number of patches" 16;
1693
1694			_result
1695				= Image (image_set_type Image_type.B_W
1696					(clip2fmt Image_format.UCHAR wedge))
1697			{
1698				wedge
1699					= 255 / (to_real npatches - 1) *
1700						(int) (strip?0 / to_real pwidth)
1701				{
1702					strip = make_xy (to_real pwidth * to_real npatches) pheight;
1703				}
1704			}
1705		}
1706	}
1707
1708	CMYK_test_chart_item = class
1709		Menuaction "_CMYK Wedges" "make a set of CMYK wedges" {
1710		action = class
1711			_result {
1712			_vislevel = 3;
1713
1714			pwidth = Expression "Patch width" 8;
1715			pheight = Expression "Patch height" 8;
1716			npatches = Expression "Number of patches" 16;
1717
1718			_result
1719				= Image (image_set_type Image_type.CMYK
1720					(clip2fmt Image_format.UCHAR strips))
1721			{
1722				wedge
1723					= 255 / (to_real npatches - 1) *
1724						(int) (strip?0 / to_real pwidth)
1725				{
1726					strip = make_xy (to_real pwidth * to_real npatches) pheight;
1727				}
1728
1729				black = wedge * 0;
1730
1731				C = wedge ++ black ++ black ++ black;
1732				M = black ++ wedge ++ black ++ black;
1733				Y = black ++ black ++ wedge ++ black;
1734				K = black ++ black ++ black ++ wedge;
1735
1736				strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]];
1737			}
1738		}
1739	}
1740
1741    Colour_atlas_item = class
1742        Menuaction "_Colour Atlas"
1743            "make a grid of patches grouped around a colour" {
1744        action = class
1745            _result {
1746            _vislevel = 3;
1747
1748            start = Colour_picker "Lab" [50,0,0];
1749            nstep = Expression "Number of steps" 2;
1750            ssize = Expression "Step size" 2;
1751
1752            _result
1753                = Image (colour_transform_to (get_type start) im)
1754            {
1755                base = colour_transform_to Image_type.LAB start;
1756                step = to_real ssize;
1757                offset = to_real nstep * step;
1758                range c = [c - offset, c - offset + step ... c + offset];
1759
1760                mk_patch b a = image_new 150 150 3
1761                    Image_format.FLOAT Image_coding.NOCODING
1762                    Image_type.LAB (Vector [base?0, a, b]) 0 0;
1763
1764                mk_strip b = map (mk_patch b) (range base?1);
1765                mk_grid = map mk_strip (range base?2);
1766                im = imagearray_assemble 15 15 mk_grid;
1767            }
1768        }
1769    }
1770}
1771
1772