From cc60419353831f2c06a248e9dd0fe9caf23ecc00 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 1 Nov 2017 15:06:08 +0000 Subject: [PATCH 01/37] add fill_nearest --- ChangeLog | 1 + share/nip2/start/_joe_extra.def | 35 +++++++++++++++++++++++++++++++++ share/nip2/start/_stdenv.def | 2 -- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index efb7090a..5e29a9af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ started 8.6.0 16/8/17 - add scRGB support - improve radiance support - add composite to alpha menu +- add Image / Select / Fill started 8.5.1 22/1/17 - fix a crash bug diff --git a/share/nip2/start/_joe_extra.def b/share/nip2/start/_joe_extra.def index 0277d261..1e7ac4aa 100644 --- a/share/nip2/start/_joe_extra.def +++ b/share/nip2/start/_joe_extra.def @@ -362,6 +362,41 @@ Select_item = class } } + Fill_item = class + Menuaction "_Fill" "fill zero pixels with the nearest non-zero" { + action x = class + Image _result { + _vislevel = 3; + + distance = Image _distance; + + [_result, _distance] = vips_call "fill_nearest" [x.value] [ + "distance" => true + ]; + } + } + +fill_nearest x + = oo_unary_function nearest_op x, is_class x + = near x, is_image x + = error (_ "bad arguments to " ++ "fill_nearest") +{ + nearest_op = Operator "fill_nearest" + fill_nearest Operator_type.COMPOUND_REWRAP false; + + near x + = [out, distance] + { + [out, distance] = vips_call "fill_nearest" [x] [ + "distance" => true + ]; + } +} + + + + + }; //////////////////////////////////////////////////////////////////////////////////// diff --git a/share/nip2/start/_stdenv.def b/share/nip2/start/_stdenv.def index a7cf8f3a..9766ddd7 100644 --- a/share/nip2/start/_stdenv.def +++ b/share/nip2/start/_stdenv.def @@ -2593,5 +2593,3 @@ hist_entropy x ]; } } - - From ea88d7f9e2660a9e6572581d31d96367ca2f01de Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 2 Nov 2017 14:35:15 +0000 Subject: [PATCH 02/37] add combine mode to indexed hist --- ChangeLog | 1 + share/nip2/start/Histogram.def | 33 ++++++++++++++++++++------------- share/nip2/start/_stdenv.def | 30 +++++++++++++++++++++++++++--- share/nip2/start/_types.def | 19 +++++++++++++++++++ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5e29a9af..51c932c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ started 8.6.0 16/8/17 - improve radiance support - add composite to alpha menu - add Image / Select / Fill +- add combine mode to indexed histogram started 8.5.1 22/1/17 - fix a crash bug diff --git a/share/nip2/start/Histogram.def b/share/nip2/start/Histogram.def index 6280f4f9..27c61b00 100644 --- a/share/nip2/start/Histogram.def +++ b/share/nip2/start/Histogram.def @@ -80,21 +80,28 @@ Hist_find_item = class Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { - action x y - = map_binary map x y - { - map a b - = hist_find_indexed index im - { - [im, index] = sortc (const is_index) [a, b]; + action x y = class + _result { + _vislevel = 3; + + combine = Combine_picker Combine_type.SUM; - is_index x - = has_image x && b == 1 && - (f == Image_format.UCHAR || f == Image_format.USHORT) + _result + = map_binary map x y + { + map a b + = hist_find_indexed combine.value index im { - im = get_image x; - b = get_bands x; - f = get_format x; + [im, index] = sortc (const is_index) [a, b]; + + is_index x + = has_image x && b == 1 && + (f == Image_format.UCHAR || f == Image_format.USHORT) + { + im = get_image x; + b = get_bands x; + f = get_format x; + } } } } diff --git a/share/nip2/start/_stdenv.def b/share/nip2/start/_stdenv.def index 9766ddd7..0112d1f7 100644 --- a/share/nip2/start/_stdenv.def +++ b/share/nip2/start/_stdenv.def @@ -1434,15 +1434,39 @@ hist_find_nD bins image (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } -hist_find_indexed index value +hist_find_indexed mode index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value - = im_hist_indexed index value, is_image index && is_image value + = indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" - (compose (compose Plot_histogram) hist_find_indexed) + (compose (compose Plot_histogram) (hist_find_indexed mode)) Operator_type.COMPOUND false; + + indexed index value + = out + { + [out] = vips_call "hist_find_indexed" [value, index] [ + "combine" => mode + ]; + } +} + +hist_entropy x + = oo_unary_function hist_entropy_op x, is_class x + = entropy x, is_image x + = error (_ "bad arguments to " ++ "hist_entropy") +{ + hist_entropy_op = Operator "hist_entropy" + hist_entropy Operator_type.COMPOUND_REWRAP false; + + entropy x + = out + { + [out] = vips_call "hist_entropy" [x] [ + ]; + } } hist_map hist image diff --git a/share/nip2/start/_types.def b/share/nip2/start/_types.def index 7f589551..d07712f6 100644 --- a/share/nip2/start/_types.def +++ b/share/nip2/start/_types.def @@ -1389,3 +1389,22 @@ Blend_picker default = class blend = Option "Blend" Blend_type.descriptions default; } +Combine_type = class { + MAX = 0; + SUM = 1; + MIN = 2; + + enum = Enum [ + _ "Maximum" => MAX, + _ "Sum" => SUM, + _ "Minimum" => MIN + ]; +} + +Combine type = class { + value = Combine_type.enum.names?type; +} + +Combine_sum = Combine Combine_type.SUM; + +Combine_picker default = Option "Combine" Combine_type.enum.names default; From 1473c1decb6d6939f6a23513fd05cd8cdaf71a9c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 3 Nov 2017 16:36:53 +0000 Subject: [PATCH 03/37] add 8.5 compat, update for combine modes see hist_find_indexed --- share/nip2/compat/8.5/Colour.def | 680 ++++++ share/nip2/compat/8.5/Filter.def | 1694 ++++++++++++++ share/nip2/compat/8.5/Histogram.def | 334 +++ share/nip2/compat/8.5/Image.def | 2272 +++++++++++++++++++ share/nip2/compat/8.5/Magick.def | 2423 ++++++++++++++++++++ share/nip2/compat/8.5/Makefile.am | 27 + share/nip2/compat/8.5/Math.def | 588 +++++ share/nip2/compat/8.5/Matrix.def | 537 +++++ share/nip2/compat/8.5/Object.def | 49 + share/nip2/compat/8.5/Preferences.ws | 919 ++++++++ share/nip2/compat/8.5/Tasks.def | 952 ++++++++ share/nip2/compat/8.5/Widgets.def | 46 + share/nip2/compat/8.5/_Object.def | 364 +++ share/nip2/compat/8.5/_convert.def | 685 ++++++ share/nip2/compat/8.5/_generate.def | 155 ++ share/nip2/compat/8.5/_joe_extra.def | 470 ++++ share/nip2/compat/8.5/_joe_utilities.def | 705 ++++++ share/nip2/compat/8.5/_list.def | 482 ++++ share/nip2/compat/8.5/_magick.def | 1107 +++++++++ share/nip2/compat/8.5/_predicate.def | 528 +++++ share/nip2/compat/8.5/_stdenv.def | 2597 ++++++++++++++++++++++ share/nip2/compat/8.5/_types.def | 1285 +++++++++++ share/nip2/compat/Makefile.am | 2 +- share/nip2/start/Preferences.ws | 2 +- 24 files changed, 18901 insertions(+), 2 deletions(-) create mode 100644 share/nip2/compat/8.5/Colour.def create mode 100644 share/nip2/compat/8.5/Filter.def create mode 100644 share/nip2/compat/8.5/Histogram.def create mode 100644 share/nip2/compat/8.5/Image.def create mode 100644 share/nip2/compat/8.5/Magick.def create mode 100644 share/nip2/compat/8.5/Makefile.am create mode 100644 share/nip2/compat/8.5/Math.def create mode 100644 share/nip2/compat/8.5/Matrix.def create mode 100644 share/nip2/compat/8.5/Object.def create mode 100644 share/nip2/compat/8.5/Preferences.ws create mode 100644 share/nip2/compat/8.5/Tasks.def create mode 100644 share/nip2/compat/8.5/Widgets.def create mode 100644 share/nip2/compat/8.5/_Object.def create mode 100644 share/nip2/compat/8.5/_convert.def create mode 100644 share/nip2/compat/8.5/_generate.def create mode 100644 share/nip2/compat/8.5/_joe_extra.def create mode 100644 share/nip2/compat/8.5/_joe_utilities.def create mode 100644 share/nip2/compat/8.5/_list.def create mode 100644 share/nip2/compat/8.5/_magick.def create mode 100644 share/nip2/compat/8.5/_predicate.def create mode 100644 share/nip2/compat/8.5/_stdenv.def create mode 100644 share/nip2/compat/8.5/_types.def diff --git a/share/nip2/compat/8.5/Colour.def b/share/nip2/compat/8.5/Colour.def new file mode 100644 index 00000000..bb2b7906 --- /dev/null +++ b/share/nip2/compat/8.5/Colour.def @@ -0,0 +1,680 @@ + +Colour_new_item = class + Menupullright (_ "_New") (_ "make a patch of colour") { + Widget_colour_item = class + Menuaction (_ "_Colour") (_ "make a patch of colour") { + action = Colour_picker "Lab" [50,0,0]; + } + + LAB_colour = class + Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { + action = widget "Lab" [50, 0, 0]; + + // ab_slice size + size = 512; + + // range of values ... +/- 128 for ab + range = 256; + + // map xy in slice image to ab and back + xy2ab x = x / (size / range) - 128; + ab2xy a = (a + 128) * (size / range); + + widget space default_value = class + Colour space _result { + _vislevel = 3; + + [_L, _a, _b] = default_value; + L = Scale "Lightness" 0 100 _L; + ab_slice = Image (lab_slice size L.value); + point = Mark ab_slice (ab2xy _a) (ab2xy _b); + + _result = [L.value, xy2ab point.left, xy2ab point.top]; + + Colour_edit colour_space value = widget colour_space value; + } + } + + CCT_colour = class + Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { + action = widget 6500; + + widget x = class + _result { + _vislevel = 3; + + T = Scale "CCT" 1800 25000 x; + + _result = colour_from_temp (to_real T); + + Colour_edit space value + = widget (temp_from_colour (Colour space value)); + } + } +} + +Colour_to_colour_item = class + Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { + action x = to_colour x; +} + +#separator + +Colour_convert_item = class + Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { + spaces = Image_type.image_colour_spaces; + + conv dest x = class + _result { + _vislevel = 3; + + to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); + + _result = map_unary (colour_transform_to to.value_thing) x; + } + + Mono_item = class + Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { + action x = conv Image_type.B_W x; + } + + sRGB_item = class + Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { + action x = conv Image_type.sRGB x; + } + + GREY16_item = class + Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { + action x = conv Image_type.GREY16 x; + } + + RGB16_item = class + Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { + action x = conv Image_type.RGB16 x; + } + + Lab_item = class + Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { + action x = conv Image_type.LAB x; + } + + LabQ_item = class + Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { + action x = conv Image_type.LABQ x; + } + + LabS_item = class + Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { + action x = conv Image_type.LABS x; + } + + LCh_item = class + Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { + action x = conv Image_type.LCH x; + } + + XYZ_item = class + Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { + action x = conv Image_type.XYZ x; + } + + Yxy_item = class + Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { + action x = conv Image_type.YXY x; + } + + UCS_item = class + Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { + action x = conv Image_type.UCS x; + } +} + +/* mark objects as being in various colourspaces + */ +Colour_tag_item = class + Menupullright (_ "_Tag As") + (_ "tag object as being in various colour spaces") { + spaces = Image_type.image_colour_spaces; + + tag dest x = class + _result { + _vislevel = 3; + + to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); + + _result = map_unary (image_set_type to.value_thing) x; + } + + Mono_item = class + Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { + action x = tag Image_type.B_W x; + } + + sRGB_item = class + Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { + action x = tag Image_type.sRGB x; + } + + RGB16_item = class + Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { + action x = tag Image_type.RGB16 x; + } + + GREY16_item = class + Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { + action x = tag Image_type.GREY16 x; + } + + Lab_item = class + Menuaction (_ "_Lab") + (_ "tag as being in Lab colourspace (float Lab)") { + action x = tag Image_type.LAB x; + } + + LabQ_item = class + Menuaction (_ "Lab_Q") + (_ "tag as being in LabQ colourspace (32-bit Lab)") { + action x = tag Image_type.LABQ x; + } + + LabS_item = class + Menuaction (_ "Lab_S") + (_ "tag as being in LabS colourspace (48-bit Lab)") { + action x = tag Image_type.LABS x; + } + + LCh_item = class + Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { + action x = tag Image_type.LCH x; + } + + XYZ_item = class + Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { + action x = tag Image_type.XYZ x; + } + + Yxy_item = class + Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { + action x = tag Image_type.YXY x; + } + + UCS_item = class + Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { + action x = tag Image_type.UCS x; + } +} + +Colour_temperature_item = class + Menupullright (_ "Te_mperature") + (_ "colour temperature conversions") { + Whitepoint_item = class + Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { + action x = class + _result { + _vislevel = 3; + + old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; + new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; + + _result + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im' * + (new_white.value_thing / old_white.value_thing); + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + D65_to_D50_item = class + Menupullright (_ "D_65 to D50") (_ "complex conversion") { + XYZ_minimal_item = class + Menuaction (_ "_Minimal") + (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = recomb D652D50_direct im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + + Bradford_item = class + Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im_D652D50 im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + D50_to_D65_item = class + Menupullright (_ "D_50 to D65") (_ "complex conversion") { + XYZ_minimal_item = class + Menuaction (_ "_Minimal") + (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = recomb D502D65_direct im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + + Bradford_item = class + Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im_D502D65 im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + Lab_to_D50XYZ_item = class + Menuaction (_ "_Lab to D50 XYZ") + (_ "Lab to XYZ with a D50 whitepoint") { + action x = map_unary (colour_unary im_D50Lab2XYZ) x; + } + + D50XYZ_to_Lab_item = class + Menuaction (_ "D50 _XYZ to Lab") + (_ "XYZ to Lab with a D50 whitepoint") { + action x = map_unary (colour_unary im_D50XYZ2Lab) x; + } + + sep1 = Menuseparator; + + CCT_item = class + Menuaction (_ "Calculate temperature") + (_ "estimate CCT using the McCamy approximation") { + action z = map_unary temp_from_colour z; + } + + Colour_item = Colour_new_item.CCT_colour; +} + +Colour_icc_item = class + Menupullright (_ "_ICC") (_ "transform with ICC profiles") { + print_profile = + "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; + monitor_profile = + "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; + guess_profile image + = print_profile, + has_type image && + get_type image == Image_type.CMYK && + has_bands image && + get_bands image >= 4 + = monitor_profile; + render_intents = Option_enum (_ "Render intent") Render_intent.names + (_ "Absolute"); + + Export_item = class + Menuaction (_ "_Export") (_ "export from PCS to device space") { + action x = class + _result { + _vislevel = 3; + + profile = Pathname (_ "Output profile") print_profile; + intent = render_intents; + depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; + + _result + = map_unary process x + { + process image + = icc_export [8, 16]?depth profile.value + intent.value_thing lab + { + lab = colour_transform_to Image_type.LABQ image; + } + } + } + } + + Import_item = class + Menuaction (_ "_Import") (_ "import from device space to PCS") { + action x = class + _result { + _vislevel = 3; + + embedded = Toggle (_ "Use embedded profile if possible") false; + profile = Pathname (_ "Default input profile") (guess_profile x); + intent = render_intents; + + _result + = map_unary process x + { + process image + = icc_import_embedded intent.value_thing image, + get_header_type "icc-profile-data" image != 0 && + embedded + = icc_import profile.value intent.value_thing image; + } + } + } + + Transform_item = class + Menuaction (_ "_Transform") (_ "transform between two device spaces") { + action x = class + _result { + _vislevel = 3; + + in_profile = Pathname (_ "Input profile") (guess_profile x); + out_profile = Pathname (_ "Output profile") print_profile; + intent = render_intents; + + _result + = map_unary process x + { + process image + = icc_transform in_profile.value out_profile.value + intent.value_thing image; + } + } + } + + AC2RC_item = class + Menuaction (_ "_Absolute to Relative") + (_ "absolute to relative colorimetry using device profile") { + action x = class + _result { + _vislevel = 3; + + profile = Pathname (_ "Pick a profile") (guess_profile x); + + _result + = map_unary process x + { + process image + = icc_ac2rc profile.value lab + { + lab = colour_transform_to Image_type.LAB image; + } + } + } + } +} + +Colour_rad_item = class + Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { + Unpack_item = class + Menuaction (_ "Unpack") + (_ "unpack Radiance format to float") { + action x = map_unary rad2float x; + } + + Pack_item = class + Menuaction (_ "Pack") + (_ "pack 3-band float to Radiance format") { + action x = map_unary float2rad x; + } +} + +#separator + +Colour_dE_item = class + Menupullright (_ "_Difference") (_ "calculate colour difference") { + /* Apply a converter to an object ... convert image or colour (since + * we can guess the colour space we're converting from), don't convert + * matrix or vector (since we can't tell ... assume it's in the right + * space already). + */ + apply_cvt cvt x + = cvt x, + is_Image x || is_Colour x || is_image x + = x; + + diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); + + /* Converter to LAB. + */ + lab_cvt = colour_transform_to Image_type.LAB; + + /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after + * to make sure we get a rectangular coord system. + */ + ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ + colour_transform_to Image_type.UCS; + + CIEdE76_item = class + Menuaction (_ "CIE dE _76") + (_ "calculate CIE dE 1976 for two objects") { + action a b = map_binary (diff lab_cvt) a b; + } + + CIEdE00_item = class + Menuaction (_ "CIE dE _00") + (_ "calculate CIE dE 2000 for two objects") { + action a b = map_binary + (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; + } + + UCS_item = class + Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { + action a b = map_binary (diff ucs_cvt) a b; + } +} + +Colour_adjust_item = class + Menupullright (_ "_Adjust") (_ "alter colours in various ways") { + Recombination_item = class + Menuaction (_ "_Recombination") + (_ "recombine colour with an editable matrix") { + action x = class + _result { + _vislevel = 3; + + matrix + = Matrix_rec (identity_matrix (bands x)) + { + // try to guess a sensible value for the size of the + // matrix + bands x + = x.bands, is_Image x || is_Colour x + = x.width, is_Matrix x + = bands x.value?0, is_Group x + = x.bands, has_member "bands" x + = 3; + } + + _result = map_unary (recomb matrix) x; + } + } + + Cast_item = class + Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { + action x = class + _result { + _vislevel = 3; + + gr = Scale "Green-red" (-20) 20 0; + by = Scale "Blue-yellow" (-20) 20 0; + + _result + = map_unary adjust_cast x + { + adjust_cast in + = colour_transform_to (get_type in) in'' + { + in' = colour_transform_to Image_type.LAB in; + in'' = in' + + Vector [0, gr.value, by.value]; + } + } + } + } + + HSB_item = class + Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { + action x = class + _result { + _vislevel = 3; + + h = Scale "Hue" 0 360 0; + s = Scale "Saturation" 0.01 5 1; + b = Scale "Brightness" 0.01 5 1; + + _result + = map_unary adjust_hsb x + { + adjust_hsb in + = colour_transform_to (get_type in) in'' + { + in' = colour_transform_to Image_type.LCH in; + in'' = in' * Vector [b.value, s.value, 1] + + Vector [0, 0, h.value]; + } + } + } + } +} + +Colour_similar_item = class + Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { + action x = class + _result { + _vislevel = 3; + + target_colour = Colour_picker "Lab" [50, 0, 0]; + t = Scale "dE threshold" 0 100 10; + + _result + = map_unary match x + { + match in + = abs_vec (in' - target) < t + { + target = colour_transform_to Image_type.LAB target_colour; + in' = colour_transform_to Image_type.LAB in; + } + } + } +} + +#separator + +Colour_chart_to_matrix_item = class + Menuaction (_ "_Measure Colour Chart") + (_ "measure average pixel values for a colour chart image") { + action x = class + _result { + _vislevel = 3; + + pacross = Expression (_ "Patches across chart") 6; + pdown = Expression (_ "Patches down chart") 4; + measure = Scale (_ "Measure area (%)") 1 100 50; + + // get a representative image from an arg + get_image x + = get_image x.value?0, is_Group x + = x; + + _im = get_image x; + sample = measure_draw (to_real pacross) (to_real pdown) + (to_real measure) _im; + + _result + = map_unary chart x + { + chart in + = measure_sample (to_real pacross) (to_real pdown) + (to_real measure) in; + } + } +} + +Colour_matrix_to_chart_item = class + Menuaction (_ "Make Synth_etic Colour Chart") + (_ "make a colour chart image from a matrix of measurements") { + action x = class + _result { + _vislevel = 3; + + pacross = Expression (_ "Patches across chart") 6; + pdown = Expression (_ "Patches down chart") 4; + pwidth = Expression (_ "Patch width in pixels") 50; + pheight = Expression (_ "Patch height in pixels") 50; + bwidth = Expression (_ "Border between patches") 0; + + _result + = map_unary build_chart x + { + build_chart in + = Image (imagearray_assemble + (to_real bwidth) (to_real bwidth) patch_table) + { + // patch numbers for row starts + rowstart = map (multiply (to_real pacross)) + [0 .. to_real pdown - 1]; + + // assemble patches ... each one a pixel value + patches = map (take (to_real pacross)) + (map (converse drop in.value) rowstart); + + // make an n-band constant image from eg. [1,2,3] + // we don't know the format .. use sRGB (well, why not?) + patch v = image_new (to_real pwidth) (to_real pheight) (len v) + Image_format.FLOAT Image_coding.NOCODING + Image_type.sRGB (Vector v) 0 0; + + // make an image for each patch + patch_table = map (map patch) patches; + } + } + } +} + +Colour_plot_ab_scatter_item = class + Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { + action x = class + _result { + _vislevel = 3; + + bins = Expression (_ "Number of bins on each axis") 8; + + _result + = map_unary plot_scatter x + { + plot_scatter in + = Image (bg * (((90 / mx) * hist) ++ blk)) + { + lab = colour_transform_to Image_type.LAB in.value; + ab = (unsigned char) ((lab?1 ++ lab?2) + 128); + hist = hist_find_nD bins.expr ab; + mx = max hist; + bg = lab_slice bins.expr 1; + blk = 1 + im_black (to_real bins) (to_real bins) 2; + } + } + } +} diff --git a/share/nip2/compat/8.5/Filter.def b/share/nip2/compat/8.5/Filter.def new file mode 100644 index 00000000..a8ac20c0 --- /dev/null +++ b/share/nip2/compat/8.5/Filter.def @@ -0,0 +1,1694 @@ +Filter_conv_item = class + Menupullright "_Convolution" "various spatial convolution filters" { + /* Some useful masks. + */ + filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; + filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; + filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; + filter_laplacian = Matrix_con 1 128 + [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; + filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; + filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; + + Blur_item = class + Menuaction "_Blur" "3x3 blur of image" { + action x = map_unary (conv filter_blur) x; + } + + Sharpen_item = class + Menuaction "_Sharpen" "3x3 sharpen of image" { + action x = map_unary (conv filter_sharp) x; + } + + Emboss_item = class + Menuaction "_Emboss" "1 pixel displace emboss" { + action x = map_unary (conv filter_emboss) x; + } + + Laplacian_item = class + Menuaction "_Laplacian" "3x3 laplacian edge detect" { + action x = map_unary (conv filter_laplacian) x; + } + + Sobel_item = class + Menuaction "So_bel" "3x3 Sobel edge detect" { + action x + = map_unary sobel x + { + sobel im + = abs (a - 128) + abs (b - 128) + { + a = conv filter_sobel im; + b = conv (rot270 filter_sobel) im; + } + } + } + +/* 3x3 line detect of image +diagonals should be scaled down by root(2) I guess +Kirk +*/ + Linedet_item = class + Menuaction "Li_ne Detect" "3x3 line detect" { + action x + = map_unary lindet x + { + lindet im + = foldr1 max_pair images + { + masks = take 4 (iterate rot45 filter_lindet); + images = map (converse conv im) masks; + } + } + } + + Usharp_item = class + Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { + action x = class + _result { + _vislevel = 3; + + size = Option "Radius" [ + "3 pixels", + "5 pixels", + "7 pixels", + "9 pixels", + "11 pixels", + "51 pixels" + ] 0; + + st = Scale "Smoothness threshold" 0 5 2; + bm = Scale "Brighten by at most" 1 50 10; + dm = Scale "Darken by at most" 1 50 20; + fs = Scale "Sharpen flat areas by" 0 5 0.5; + js = Scale "Sharpen jaggy areas by" 0 5 1; + + _result + = map_unary process x + { + process in + = Image in''' + { + in' = colour_transform_to Image_type.LABS in.value; + in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; + in''' = colour_transform_to (get_type in) in''; + } + } + } + } + + sep1 = Menuseparator; + + Custom_blur_item = class + Menuaction "Custom B_lur / Sharpen" + "blur or sharpen with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + type = Option "Type" ["Blur", "Sharpen"] 0; + r = Scale "Radius" 1 100 1; + fac = Scale "Amount" 0 1 1; + layers = Scale "Layers" 1 100 10; + shape = Option "Mask shape" [ + "Square", + "Gaussian" + ] 0; + prec = Option "Precision" ["Int", "Float", "Approximate"] 0; + + _result + = map_unary process x + { + process in + = clip2fmt blur.format proc + { + mask + = matrix_blur r.value, shape.value == 0 + = matrix_gaussian_blur r.value; + blur = [convsep, convsepf, aconvsep layers]?prec mask in; + proc + = in + fac * (in - blur), type == 1 + = blur * fac + in * (1 - fac); + } + } + } + } + + Custom_conv_item = class + Menuaction "Custom C_onvolution" + "convolution filter with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; + separable + = Toggle "Seperable convolution" false, + matrix.width == 1 || matrix.height == 1 + = false; + type = Option "Convolution type" ["Int", "Float"] 0; + rotate = Option "Rotate" [ + "Don't rotate", + "4 x 45 degrees", + "8 x 45 degrees", + "2 x 90 degrees" + ] 0; + + _result + = map_unary process x + { + process in + = in.Image in' + { + conv_fn + = im_lindetect, !separable && type == 0 && rotate == 1 + = im_compass, !separable && type == 0 && rotate == 2 + = im_gradient, !separable && type == 0 && rotate == 3 + = im_conv, !separable && type == 0 + = im_convsep, separable && type == 0 + = im_conv_f, !separable && type == 1 + = im_convsep_f, separable && type == 1 + = error "boink!"; + in' = conv_fn in.value matrix; + } + } + } + } +} + +Filter_rank_item = class + Menupullright "_Rank" "various rank filters" { + Median_item = class + Menuaction "_Median" "3x3 median rank filter" { + action x = map_unary (rank 3 3 4) x; + } + + Image_rank_item = class + Menuaction "_Image Rank" "pixelwise rank a list or group of images" { + action x = class + _result { + _vislevel = 3; + + select + = Expression "Rank" ((int) (guess_size / 2)) + { + guess_size + = len x, is_list x + = len x.value, is_Group x + = 0; + } + + // can't really iterate over groups ... since we allow a group + // argument + _result = rank_image select x; + } + } + + Custom_rank_item = class + Menuaction "Custom _Rank" "rank filter with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + window_width = Expression "Window width" 3; + window_height = Expression "Window height" 3; + select = Expression "Rank" + ((int) ((to_real window_width * to_real window_height) / 2)); + + _result + = map_unary process x + { + process in + = rank window_width window_height select in; + } + } + } +} + +Filter_morphology_item = class + Menupullright "_Morphology" "various morphological filters" { + /* Some useful masks. + */ + mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; + mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; + mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; + thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; + + Threshold_item = Select_item.Threshold_item; + + sep1 = Menuseparator; + + Dilate_item = class + Menupullright "_Dilate" "morphological dilate" { + Dilate8_item = class + Menuaction "_8-connected" "dilate with an 8-connected mask" { + action x = map_unary (dilate mask8) x; + } + + Dilate4_item = class + Menuaction "_4-connected" "dilate with a 4-connected mask" { + action x = map_unary (dilate mask4) x; + } + } + + Erode_item = class + Menupullright "_Erode" "morphological erode" { + Erode8_item = class + Menuaction "_8-connected" "erode with an 8-connected mask" { + action x = map_unary (erode mask8) x; + } + + Erode4_item = class + Menuaction "_4-connected" "erode with a 4-connected mask" { + action x = map_unary (erode mask4) x; + } + } + + Custom_morph_item = class + Menuaction "Custom _Morphology" + "convolution morphological operator" { + action x = class + _result { + _vislevel = 3; + + mask = mask4; + type = Option "Operation" ["Erode", "Dilate"] 1; + apply = Expression "Number of times to apply mask" 1; + + _result + = map_unary morph x + { + morph image + = Image value' + { + fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); + + value' + = im_erode image.value fatmask, type.value == 0 + = im_dilate image.value fatmask; + } + } + } + } + + sep2 = Menuseparator; + + Open_item = class + Menuaction "_Open" "open with an 8-connected mask" { + action x = map_unary (dilate mask8 @ erode mask8) x; + } + + Close_item = class + Menuaction "_Close" "close with an 8-connected mask" { + action x = map_unary (erode mask8 @ dilate mask8) x; + } + + Clean_item = class + Menuaction "C_lean" "remove 8-connected isolated points" { + action x + = map_unary clean x + { + clean x = x ^ erode mask1 x; + } + } + + Thin_item = class + Menuaction "_Thin" "thin once" { + action x + = map_unary thinall x + { + masks = take 8 (iterate rot45 thin); + thin1 m x = x ^ erode m x; + thinall x = foldr thin1 x masks; + } + } + +} + +Filter_fourier_item = class + Menupullright "_Fourier" "various Fourier filters" { + preview_size = 64; + + sense_option = Option "Sense" [ + "Pass", + "Reject" + ] 0; + + // make a visualisation image + make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) + (im_create_fmask preview_size preview_size); + + // make the process function + process fn in + = (Image @ fn) (im_flt_image_freq in.value); + + New_ideal_item = class + Menupullright "_Ideal" "various ideal Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f sense.value fc.value 0 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 6) fc.value + rw.value 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 12) fcx.value fcy.value + r.value 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } + + New_gaussian_item = class + Menupullright "_Gaussian" "various Gaussian Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 4) fc.value + ac.value 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 10) fc.value + rw.value ac.value 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 16) fcx.value fcy.value + r.value ac.value 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } + + New_butterworth_item = class + Menupullright "_Butterworth" + "various Butterworth Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 2) o.value fc.value ac.value + 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 8) o.value fc.value rw.value + ac.value 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 14) o.value fcx.value fcy.value + r.value ac.value; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } +} + +Filter_enhance_item = class + Menupullright "_Enhance" "various enhancement filters" { + Falsecolour_item = class + Menuaction "_False Colour" "false colour a mono image" { + action x = class + _result { + _vislevel = 3; + + o = Scale "Offset" (-255) 255 0; + clip = Toggle "Clip colour range" false; + + _result + = map_unary process x + { + process im + = falsecolour mono'' + { + mono = colour_transform_to Image_type.B_W im; + mono' = mono + o; + mono'' + = (unsigned char) mono', clip + = (unsigned char) (mono' & 0xff); + } + } + } + } + + Statistical_diff_item = class + Menuaction "_Statistical Difference" + "statistical difference of an image" { + action x = class + _result { + _vislevel = 3; + + wsize = Expression "Window size" 11; + tmean = Expression "Target mean" 128; + mean_weight = Scale "Mean weight" 0 1 0.8; + tdev = Expression "Target deviation" 50; + dev_weight = Scale "Deviation weight" 0 1 0.8; + border = Toggle "Output image matches input image in size" true; + + _result + = map_unary process x + { + process in + = Image in'' + { + in' = colour_transform_to Image_type.B_W in.value; + fn + = im_stdif, border + = im_stdif_raw; + in'' = fn in' + mean_weight.value tmean.expr + dev_weight.value tdev.expr + wsize.expr wsize.expr; + } + } + } + } + + Hist_equal_item = class + Menupullright "_Equalise Histogram" "equalise contrast" { + Global_item = class + Menuaction "_Global" "equalise contrast globally" { + action x = map_unary hist_equalize x; + } + + Local_item = class + Menuaction "_Local" "equalise contrast within a roving window" { + action x = class + _result { + _vislevel = 3; + + window_width = Expression "Window width" 20; + window_height = Expression "Window height" 20; + max_slope = Scale "Maxium slope" 0 10 0; + + _result + = map_unary process x + { + process in + = hist_equalize_local + window_width + window_height + max_slope in; + } + } + } + } +} + +Filter_correlate_item = class + Menupullright "Spatial _Correlation" "calculate correlation surfaces" { + Correlate_item = class + Menuaction "_Correlate" "calculate correlation coefficient" { + action a b + = map_binary corr a b + { + corr a b + = correlate a b, + a.width <= b.width && a.height <= b.height + = correlate b a; + } + } + + Correlate_fast_item = class + Menuaction "_Simple Difference" + "calculate sum of squares of differences" { + action a b + = map_binary corr a b + { + corr a b + = correlate_fast a b, + a.width <= b.width && a.height <= b.height + = correlate_fast b a; + } + } +} + +Filter_hough_item = class + Menupullright "_Hough Transform" "transform to parameter space" { + Line_item = class + Menuaction "_Line" "find straight line Hough transform" { + action a = class + _result { + _vislevel = 3; + + pspace_width = Expression "Parameter space width" 64; + pspace_height = Expression "Parameter space height" 64; + + _result + = map_unary line a + { + line a + = hough_line + (to_real pspace_width) (to_real pspace_height) a; + } + } + } + + Circle_item = class + Menuaction "_Circle" "find circle Hough transform" { + action a = class + _result { + _vislevel = 3; + + scale = Expression "Scale down parameter space by" 10; + min_radius = Expression "Minimum radius" 10; + max_radius = Expression "Maximum radius" 30; + + _result + = map_unary circle a + { + circle a + = hough_circle (to_real scale) (to_real min_radius) + (to_real max_radius) a; + } + } + } +} + +Filter_coordinate_item = class + Menupullright "_Coordinate Transform" "various coordinate transforms" { + // run a function which wants a complex arg on a non-complex two-band + // image + run_cmplx fn x + = re x' ++ im x' + { + x' = fn (x?0, x?1); + } + + Polar_item = class + Menuaction "_Polar" "transform to polar coordinates" { + action a = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary to_polar a + { + to_polar im + = mapim interp.value map' im + { + // xy image, origin in the centre, scaled to fit image to + // a circle + xy = make_xy im.width im.height; + xy' = xy - Vector [im.width / 2, im.height / 2]; + scale = min [im.width, im.height] / im.width; + xy'' = 2 * xy' / scale; + + // to polar, scale vertical axis to 360 degrees + map = run_cmplx polar xy''; + map' = map * Vector [1, im.height / 360]; + } + } + } + } + + Rectangular_item = class + Menuaction "_Rectangular" "transform to rectangular coordinates" { + action a = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary to_rect a + { + to_rect im + = mapim interp.value map'' im + { + // xy image, vertical scaled to 360 degrees + xy = make_xy im.width im.height; + xy' = xy * Vector [1, 360 / im.height]; + + // to rect, scale to image rect + map = run_cmplx rectangular xy'; + scale = min [im.width, im.height] / im.width; + map' = map * scale / 2; + + map'' = map' + Vector [im.width / 2, im.height / 2]; + } + } + } + } +} + +#separator + +Filter_tilt_item = class + Menupullright "Ti_lt Brightness" "tilt brightness" { + Left_right_item = class + Menuaction "_Left to Right" "linear left-right brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Left-right tilt" (-1) 1 0; + + _result + = map_unary tilt_lr x + { + tilt_lr image + = image * scale + { + ramp = im_fgrey image.width image.height; + scale = (ramp - 0.5) * tilt + 1; + } + } + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "linear top-bottom brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Top-bottom tilt" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + ramp = rot90 + (im_fgrey image.height image.width); + scale = (ramp - 0.5) * tilt + 1; + } + } + } + } + + sep1 = Menuseparator; + + Left_right_cos_item = class + Menuaction "Cosine Left-_right" "cosine left-right brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Left-right tilt" (-1) 1 0; + shift = Scale "Shift by" (-1) 1 0; + + _result + = map_unary tilt_lr x + { + tilt_lr image + = image * scale + { + ramp = im_fgrey image.width image.height - 0.5 - + shift.value; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } + + Top_bottom_cos_item = class + Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Top-bottom tilt" (-1) 1 0; + shift = Scale "Shift by" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + ramp = rot90 (im_fgrey image.height image.width) - 0.5 - + shift.value; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } + + sep2 = Menuseparator; + + Circular_item = class + Menuaction "_Circular" "circular brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Tilt" (-1) 1 0; + hshift = Scale "Horizontal shift by" (-1) 1 0; + vshift = Scale "Vertical shift by" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + hramp = im_fgrey image.width image.height - 0.5 - + hshift.value; + vramp = rot90 (im_fgrey image.height image.width) - 0.5 - + vshift.value; + ramp = (hramp ** 2 + vramp ** 2) ** 0.5; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } +} + +Filter_blend_item = class + Menupullright "_Blend" "blend objects together" { + Scale_blend_item = class + Menuaction "_Scale" "blend two objects together with a scale" { + action a b = class + _result { + _vislevel = 3; + + p = Scale "Blend position" 0 1 0.5; + + _result + = map_binary process a b + { + process im1 im2 = im1 * (1 - p.value) + im2 * p.value; + } + } + } + + Image_blend_item = class + Menuaction "_Image" "use an image to blend two objects" { + action a b c = class + _result { + _vislevel = 3; + + i = Toggle "Invert mask" false; + + _result + = map_trinary process a b c + { + process a b c + = blend condition in1 in2, !i + = blend (invert condition) in1 in2 + { + compare a b + // prefer image as the condition + = false, + !has_image a && has_image b + // prefer mono images as the condition + = false, + has_bands a && has_bands b && + get_bands a > 1 && get_bands b == 1 + // prefer uchar as the condition + = false, + has_format a && has_format b && + get_format a > Image_format.UCHAR && + get_format b == Image_format.UCHAR + = true; + [condition, in1, in2] = sortc compare [a, b, c]; + } + } + } + } + + Line_blend_item = class + Menuaction "_Along Line" + "blend between image a and image b along a line" { + action a b = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Left to Right", + "Top to Bottom" + ] 0; + blend_position = Scale "Blend position" 0 1 0.5; + blend_width = Scale "Blend width" 0 1 0.05; + + _result + = map_binary process a b + { + process a b + = blend (Image condition) b a + { + output_width = max_pair a.width b.width; + output_height = max_pair a.height b.height; + range + = output_width, orientation == 0 + = output_height; + blend_position' + = floor (range * blend_position.value); + blend_width' + = 1, blend_width.value == 0 + = floor (range * blend_width.value); + start = blend_position' - blend_width' / 2; + + background = (make_xy output_width output_height) >= + blend_position'; + ramp + = im_grey blend_width' output_height, orientation == 0 + = rot90 (im_grey blend_width' output_width); + condition + = insert_noexpand start 0 ramp background?0, + orientation == 0 + = insert_noexpand 0 start ramp background?1; + } + } + } + } + + Blend_alpha_item = class + Menuaction "Blend _Alpha" "blend images with optional alpha channels" { + // usage: layerit foreground background + // input images must be either 1 or 3 bands, optionally + 1 band + // which is used as the alpha channel + // rich lott + + scale_mask im opacity + = (unsigned char) (to_real opacity / 255 * im); + + // to mono + intensity = colour_transform_to Image_type.B_W; + + // All the blend functions + // I am grateful to this page + // http://www.pegtop.net/delphi/blendmodes/ + // for most of the formulae. + + blend_normal mask opacity fg bg + = blend (scale_mask mask opacity) fg bg; + + blend_iflighter mask opacity fg bg + = blend (if fg' > bg' then mask' else 0) fg bg + { + fg' = intensity fg; + bg' = intensity bg; + mask' = scale_mask mask opacity ; + } + + blend_ifdarker mask opacity fg bg + = blend (if fg' < bg' then mask' else 0) fg bg + { + fg' = intensity fg ; + bg' = intensity bg ; + mask' = scale_mask mask opacity ; + } + + blend_multiply mask opacity fg bg + = blend (scale_mask mask opacity) fg' bg + { + fg' = fg / 255 * bg; + } + + blend_add mask opacity fg bg + = blend mask fg' bg + { + fg' = opacity / 255 * fg + bg; + } + + blend_subtract mask opacity fg bg + = blend mask fg' bg + { + fg' = bg - opacity / 255 * fg; + } + + blend_screen mask opacity fg bg + = blend mask fg' bg + { + fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; + } + + blend_burn mask opacity fg bg + = blend mask fg'' bg + { + // fades to white which has no effect. + fg' = (255 - opacity) + opacity * fg / 255; + fg'' = 255 - 255 * (255 - bg) / fg'; + } + + blend_softlight mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; + } + + blend_hardlight mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' + = 2 / 255 * fg * bg, bg < 129 + = 255 - 2 * (255 - bg) * (255 - fg) / 255; + } + + blend_lighten mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = if bg < fg then fg else bg; + } + + blend_darken mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = if bg > fg then fg else bg; + } + + blend_dodge mask opacity fg bg + = blend mask fg'' bg + { + // one added to avoid divide by zero + fg' = 1 + 255 - (opacity / 255 * fg); + fg'' = bg * 255 / fg'; + } + + blend_reflect mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = bg * bg / (255 - fg); + } + + blend_freeze mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); + } + + blend_or mask opacity fg bg + = bg | (unsigned char) fg' + { + mask' = scale_mask mask opacity; + fg' = fg * mask' / 255; + } + + blend_and mask opacity fg bg + = bg & (unsigned char) fg' + { + mask' = scale_mask mask opacity; + fg' = fg * mask' / 255; + } + + // blend types + NORMAL = 0; + IFLIGHTER = 1; + IFDARKER = 2; + MULTIPLY = 3; + ADD = 4; + SUBTRACT = 5; + SCREEN = 6; + BURN = 7; + DODGE = 8; + HARDLIGHT = 9; + SOFTLIGHT = 10; + LIGHTEN = 11; + DARKEN = 12; + REFLECT = 13; + FREEZE = 14; + OR = 15; + AND = 16; + + // names we show the user for blend types + names = Enum [ + _ "Normal" => NORMAL, + _ "If Lighter" => IFLIGHTER, + _ "If Darker" => IFDARKER, + _ "Multiply" => MULTIPLY, + _ "Add" => ADD, + _ "Subtract" => SUBTRACT, + _ "Screen" => SCREEN, + _ "Burn" => BURN, + _ "Soft Light" => SOFTLIGHT, + _ "Hard Light" => HARDLIGHT, + _ "Lighten" => LIGHTEN, + _ "Darken" => DARKEN, + _ "Dodge" => DODGE, + _ "Reflect" => REFLECT, + _ "Freeze" => FREEZE, + _ "Bitwise OR" => OR, + _ "Bitwise AND" => AND + ]; + + // functions we call for each blend type + actions = Table [ + [NORMAL, blend_normal], + [IFLIGHTER, blend_iflighter], + [IFDARKER, blend_ifdarker], + [MULTIPLY, blend_multiply], + [ADD, blend_add], + [SUBTRACT, blend_subtract], + [SCREEN, blend_screen], + [BURN, blend_burn], + [SOFTLIGHT, blend_softlight], + [HARDLIGHT, blend_hardlight], + [LIGHTEN, blend_lighten], + [DARKEN, blend_darken], + [DODGE, blend_dodge], + [REFLECT, blend_reflect], + [FREEZE, blend_freeze], + [OR, blend_or], + [AND, blend_and] + ]; + + // make sure im has an alpha channel (set opaque if it hasn't) + put_alpha im + = im, im.bands == 4 || im.bands == 2 + = im ++ 255; + + // make sure im has no alpha channel + lose_alpha im + = extract_bands 0 3 im, im.bands == 4 + = im?0, im.bands == 2 + = im; + + // does im have al alpha channel? + has_alpha im = im.bands == 2 || im.bands == 4; + + // get the alpha (set opaque if no alpha) + get_alpha img + = img'?3, img.bands == 4 + = img'?1 + { + img' = put_alpha img; + } + + // add an alpha ... cast the alpha image to match the main image + append_alpha im alpha + = im ++ clip2fmt im.format alpha; + + // makes fg the same size as bg, displaced with u, v pixel offset + moveit fg bg u v + = insert_noexpand u v fg bg' + { + bg' = image_new bg.width bg.height + fg.bands fg.format fg.coding fg.type 0 0 0; + } + + action bg fg = class + _value { + _vislevel = 3; + + method = Option_enum "Blend mode" names "Normal"; + opacity = Scale "Opacity" 0 255 255; + hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; + vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; + + _value + = append_alpha blended merged_alpha, has_alpha bg + = blended + { + // displace and resize fg (need to displace alpha too) + fg' = moveit (put_alpha fg) bg hmove vmove; + + // transform to sRGB + fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); + bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); + + // alphas merged + merged_alpha = get_alpha bg | get_alpha fg'; + + // blend together + blended = (actions.lookup 0 1 method.value_thing) + (get_alpha fg') opacity.value fg'' bg'; + } + } + } +} + +Filter_overlay_header_item = class + Menuaction "_Overlay" + "make a colour overlay of two monochrome images" { + action a b = class + _result { + _vislevel = 3; + + colour = Option "Colour overlay as" [ + _ "Green over Red", + _ "Blue over Red", + _ "Red over Green", + _ "Red over Blue", + _ "Blue over Green", + _ "Green over Blue" + ] 0; + + _result + = map_binary overlay a b + { + overlay a b + = image_set_type Image_type.sRGB + [(a' ++ b' ++ 0), + (a' ++ 0 ++ b'), + (b' ++ a' ++ 0), + (b' ++ 0 ++ a'), + (0 ++ a' ++ b'), + (0 ++ b' ++ a')]?colour + { + a' = colour_transform_to Image_type.B_W a; + b' = colour_transform_to Image_type.B_W b; + } + } + } +} + +Filter_colourize_item = class + Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { + action a b = class + _result { + _vislevel = 3; + + tint = Scale "Tint" 0 1 0.6; + + _result + = map_binary tintit a b + { + tintit a b + = colour_transform_to (get_type colour) colourized' + { + // get the mono thing first + [mono, colour] = + sortc (const (is_colour_type @ get_type)) [a, b]; + + colour' = tint * colour_transform_to Image_type.LAB colour; + mono' = colour_transform_to Image_type.B_W mono; + colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; + colourized' = image_set_type Image_type.LAB colourized; + } + } + } +} + +Filter_browse_multiband_item = class + Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { + Bandwise_item = class + Menuaction "B_andwise" "browse through the bands of a multiband image" { + action image = class + _result { + _vislevel = 3; + + band = Scale "Band" 0 (image.bands - 1) 0; + display = Option "Display as" [ + _ "Grey", + _ "Green over Red", + _ "Blue over Red", + _ "Red over Green", + _ "Red over Blue", + _ "Blue over Green", + _ "Green over Blue" + ] 0; + + _result + = output + { + down = (int) band.value; + up = down + 1; + remainder = band.value - down; + + fade x a + = Vector [0], x == 0 + = a * x; + + a = fade remainder image?up; + b = fade (1 - remainder) image?down; + + output = [ + a + b, + a ++ b ++ 0, + a ++ 0 ++ b, + b ++ a ++ 0, + b ++ 0 ++ a, + 0 ++ a ++ b, + 0 ++ b ++ a + ] ? display; + } + } + } + + Bitwise_item = class + Menuaction "Bi_twise" "browse through the bits of an image" { + action x = class + _result { + _vislevel = 3; + + bit + = Islider "Bit" 0 (nbits - 1) (nbits - 1) + { + nbits + = x.bits, is_Image x + = 8; + Islider c f t v = class + scope.Scale c f t ((int) v) { + Scale = Islider; + } + } + + _result + = map_unary process x + { + process im = (im & (0x1 << bit.value)) != 0; + } + } + } +} + +#separator + +Filter_negative_item = class + Menuaction "Photographic _Negative" "swap black and white" { + action x + = map_unary invert x + { + invert in + = clip2fmt in.format (colour_transform_to (get_type in) rgb') + { + rgb = colour_transform_to Image_type.sRGB in; + rgb' = 255 - rgb; + } + } +} + +Filter_solarize_item = class + Menuaction "_Solarise" "invert colours above a threshold" { + action x = class + _result { + _vislevel = 3; + + kink = Scale "Kink" 0 1 0.5; + + _result + = map_unary process x + { + process image + = hist_map tab'''' image + { + // max pixel value for this format + mx = Image_format.maxval image.format; + + // make a LUT ... just 8 and 16 bit + tab + = im_identity_ushort image.bands mx, + image.format == + Image_format.USHORT + = im_identity image.bands; + tab' = Image tab; + + // make basic ^ shape + tab'' + = tab' * (1 / kink), tab' < mx * kink + = (mx - tab') / (1 - kink); + tab''' = clip2fmt image.format tab''; + + // smooth a bit + mask = matrix_blur (tab'''.width / 8); + tab'''' = convsep mask tab'''; + } + } + } +} + +Filter_diffuse_glow_item = class + Menuaction "_Diffuse Glow" "add a halo to highlights" { + action x = class + _result { + _vislevel = 3; + + r = Scale "Radius" 0 50 5; + highlights = Scale "Highlights" 0 100 95; + glow = Scale "Glow" 0 1 0.5; + colour = Colour_new_item.Widget_colour_item.action; + + _result + = map_unary process x + { + process image + = image' + { + mono = (unsigned char) (colour_transform_to + Image_type.B_W image); + thresh = hist_thresh (highlights.value / 100) mono; + mask = mono > thresh; + blur = convsep (matrix_gaussian_blur r.value) mask; + colour' = colour_transform_to image.type colour; + image' = image + colour' * glow * (blur / 255); + } + } + } +} + +Filter_drop_shadow_item = class + Menuaction "Drop S_hadow" "add a drop shadow to an image" { + action x = class + _result { + _vislevel = 3; + + sx = Scale "Horizontal shadow" (-50) 50 5; + sy = Scale "Vertical shadow" (-50) 50 5; + ss = Scale "Shadow softness" 0 20 5; + bg_colour = Expression "Background colour" 255; + sd_colour = Expression "Shadow colour" 128; + alpha = Toggle "Shadow in alpha channel" false; + transparent = Toggle "Zero pixels are transparent" false; + + _result + = map_unary shadow x + { + shadow image + = Image final + { + blur_size = ss.value * 2 + 1; + + // matrix we blur with to soften shadows + blur_matrix = matrix_gaussian_blur blur_size; + matrix_size = blur_matrix.width; + matrix_radius = (int) (matrix_size / 2) + 1; + + // position and size of shadow image in input cods + // before and after fuzzing + shadow_rect = Rect sx.value sy.value + image.width image.height; + fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; + + // size and pos of final image, in input cods + final_rect = image.rect.union fuzzy_shadow_rect; + + // hard part of shadow in output cods + shadow_rect' = Rect + (shadow_rect.left - final_rect.left) + (shadow_rect.top - final_rect.top) + shadow_rect.width shadow_rect.height; + + // make the shadow mask ... true for parts which cast + // a shadow + mask + = (foldr1 bitwise_and @ bandsplit) (image.value != 0), + transparent + = image_new image.width image.height 1 Image_format.UCHAR + Image_coding.NOCODING Image_type.B_W 255 0 0; + mask' = embed 0 shadow_rect'.left shadow_rect'.top + final_rect.width final_rect.height mask; + mask'' = convsep blur_matrix mask'; + + // use mask to fade between bg and shadow colour + mk_background colour = image_new + final_rect.width final_rect.height + image.bands image.format image.coding image.type + colour 0 0; + + bg_image = mk_background bg_colour.expr; + shadow_image = mk_background sd_colour.expr; + bg = blend mask'' shadow_image bg_image; + + // make a full size mask + fg_mask = embed 0 + (image.rect.left - final_rect.left) + (image.rect.top - final_rect.top) + final_rect.width final_rect.height mask; + + // wrap up the input image ... put the shadow colour + // around it, so if we are outputting a separate + // alpha the shadow colour will be set correctly + fg = insert (image.rect.left - final_rect.left) + (image.rect.top - final_rect.top) + image.value shadow_image; + + final + // make a separate alpha + = fg ++ mask'', alpha + + // paste image over shadow + = if fg_mask then fg else bg; + } + } + } +} + +Filter_paint_text_item = class + Menuaction "_Paint Text" "paint text into an image" { + action x + = paint_position, is_Group x + = paint_area + { + paint_area = class + _result { + _check_args = [ + [x, "x", check_Image] + ]; + _vislevel = 3; + + text = String "Text to paint" "Hello world!"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + align = Option "Alignment" ["Left", "Centre", "Right"] 0; + dpi = Expression "DPI" 300; + colour = Expression "Text colour" 255; + place = Region x (x.width / 4) (x.height / 4) + (x.width / 2) (x.height / 2); + + _result + = insert_noexpand place.left place.top (blend txt' fg place) x + { + fg = image_new place.width place.height x.bands x.format + x.coding x.type colour.expr 0 0; + txt = Image (im_text text.value font.value + place.width align.value (to_real dpi)); + bg = im_black place.width place.height 1; + txt' = insert_noexpand 0 0 txt bg; + } + } + + paint_position = class + _result { + _vislevel = 3; + + text = Pattern_images_item.Text_item.action; + colour = Expression "Text colour" 255; + position = Option "Position" [ + _ "North-west", + _ "North", + _ "North-east", + _ "West", + _ "Centre", + _ "East", + _ "South-west", + _ "South", + _ "South-east", + _ "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_unary paint x + { + paint image + = insert_noexpand x' y' place' image + { + xr = image.width - text.width; + yr = image.height - text.height; + x + = left.expr, position == 9 + = [0, xr / 2, xr]?(position % 3); + y + = top.expr, position == 9 + = [0, yr / 2, yr]?(position / 3); + x' = range 0 x (image.width - 1); + y' = range 0 y (image.height - 1); + w' = range 1 text.width (image.width - x'); + h' = range 1 text.height (image.height - y'); + + place = extract_area x' y' w' h' image; + text' = insert_noexpand 0 0 text (im_black w' h' 1); + fg = image_new w' h' image.bands image.format + image.coding image.type colour.expr 0 0; + place' = blend text' fg place; + } + } + } + } +} + +Autotrace_item = class + Menuaction "_Trace" "convert a bitmap to an SVG file" { + action x = class + _result { + _vislevel = 3; + + despeckle = Scale "Despeckle level" 1 20 1; + line = Scale "Line threshold" 1 20 1; + center = Toggle "Trace centreline" false; + scale = Scale "SVG scale" 0.1 10 1; + + command + = "autotrace %s " ++ join_sep " " + [ofmt, ofile, desp, lint, cent] + { + prog = search_for_error "autotrace"; + ofmt = "-output-format svg"; + ofile = "-output-file %s"; + desp = "-despeckle-level " ++ print despeckle.value; + lint = "-line-threshold " ++ print line.value; + cent = if center then "-centerline " else ""; + } + + _result + = Image output + { + [output] = vips_call "system" + [command] + [$in => [x.value], + $in_format => "%s.ppm", + $out => true, + $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" + ]; + } + } +} + diff --git a/share/nip2/compat/8.5/Histogram.def b/share/nip2/compat/8.5/Histogram.def new file mode 100644 index 00000000..6280f4f9 --- /dev/null +++ b/share/nip2/compat/8.5/Histogram.def @@ -0,0 +1,334 @@ +Hist_new_item = class + Menupullright "_New" "new histogram" { + Hist_item = class + Menuaction "_Identity" "make an identity histogram" { + action = class + _result { + _vislevel = 3; + + d = Option "Depth" ["8 bit", "16 bit"] 0; + _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); + } + } + + Hist_new_from_matrix = Matrix_buildlut_item; + + Hist_from_image_item = class + Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { + action x = hist_tag x; + } + + Tone_item = class + Menuaction "_Tone Curve" "make a new tone mapping curve" { + action = class + _result { + _vislevel = 3; + + d = Option "Depth" ["8 bit", "16 bit"] 0; + b = Scale "Black point" 0 100 0; + w = Scale "White point" 0 100 100; + + sp = Scale "Shadow point" 0.1 0.3 0.2; + mp = Scale "Mid-tone point" 0.4 0.6 0.5; + hp = Scale "Highlight point" 0.7 0.9 0.8; + + sa = Scale "Shadow adjust" (-15) 15 0; + ma = Scale "Mid-tone adjust" (-30) 30 0; + ha = Scale "Highlight adjust" (-15) 15 0; + + _result + = tone_build fmt b w sp mp hp sa ma ha + { + fmt = [Image_format.UCHAR, Image_format.USHORT]?d; + } + } + } +} + +Hist_convert_to_hist_item = class + Menuaction "Con_vert to Histogram" "convert anything to a histogram" { + action x = hist_tag (to_image x); +} + +Hist_find_item = class + Menupullright "_Find" "find a histogram" { + Oned_item = class + Menuaction "_One Dimension" + "for a n-band image, make an n-band 1D histogram" { + action x = map_unary hist_find x; + } + + Nd_item = class + Menuaction "_Many Dimensions" + "for a n-band image, make an n-dimensional histogram" { + action x = class + _result { + _vislevel = 3; + + // default to something small-ish + bins = Expression "Number of bins in each dimension" 8; + + _result + = map_unary process x + { + process in + = hist_find_nD bins in; + } + } + } + + Indexed_item = class + Menuaction "_Indexed" + "use a 1-band index image to pick bins for an n-band image" { + action x y + = map_binary map x y + { + map a b + = hist_find_indexed index im + { + [im, index] = sortc (const is_index) [a, b]; + + is_index x + = has_image x && b == 1 && + (f == Image_format.UCHAR || f == Image_format.USHORT) + { + im = get_image x; + b = get_bands x; + f = get_format x; + } + } + } + } +} + +Hist_map_item = class + Menuaction "_Map" "map an image through a histogram" { + action x y + = map_binary map x y + { + map a b + = hist_map hist im + { + [im, hist] = sortc (const is_hist) [a, b]; + } + } +} + +Hist_eq_item = Filter_enhance_item.Hist_equal_item; + +#separator + +Hist_cum_item = class + Menuaction "_Integrate" + "form cumulative histogram" { + action x = map_unary hist_cum x; +} + +Hist_diff_item = class + Menuaction "_Differentiate" + "find point-to-point differences (inverse of Integrate)" { + action x = map_unary hist_diff x; +} + +Hist_norm_item = class + Menuaction "N_ormalise" "normalise a histogram" { + action x = map_unary hist_norm x; +} + +Hist_inv_item = class + Menuaction "In_vert" "invert a histogram" { + action x = map_unary hist_inv x; +} + +Hist_match_item = class + Menuaction "Ma_tch" + "find LUT which will match first histogram to second" { + action in ref = map_binary hist_match in ref; +} + +Hist_zerox_item = class + Menuaction "_Zero Crossings" "find zero crossings" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Direction" [ + "Positive-going", + "Negative-going" + ] 0; + + _result + = map_unary (zerox (if edge == 0 then -1 else 1)) x; + } +} + +Hist_entropy_item = class Menuaction "Entropy" "calculate histogram entropy" { + action x = hist_entropy x; +} + +#separator + +Hist_profile_item = class + Menuaction "Find _Profile" + "search from image edges for non-zero pixels" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Search from" [ + "Top edge down", + "Left edge to right", + "Bottom edge up", + "Right edge to left" + ] 2; + + _result + = map_unary profile x + { + profile image + = (Plot_histogram @ hist_tag) [ + profilemb 0 image.value, + profilemb 1 image.value, + profilemb 0 (fliptb image.value), + profilemb 1 (fliplr image.value) + ]?edge; + + // im_profile only does 1 band images :-( + profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; + } + } +} + +Hist_project_item = class + Menuaction "Find Pro_jections" + "find horizontal and vertical projections" { + action x = class { + _vislevel = 2; + + _result = map_unary project x; + + // extract the result ... could be a group + extr n + = Plot_histogram _result?n, is_list _result + = Group (map (Plot_histogram @ converse subscript n) _result.value); + + horizontal = extr 0; + vertical = extr 1; + centre = (gravity horizontal, gravity vertical); + } +} + +#separator + +Hist_graph_item = class + Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { + action x = class + _value { + _vislevel = 3; + + width = Scale "Width" 1 40 1; + displace = Scale "Horizontal displace" (-50) 50 0; + vdisplace = Scale "Vertical displace" (-50) 50 0; + + _value + = map_unary graph x + { + graph arrow + = hist_tag area' + { + area = extract_arrow + displace.value vdisplace.value width.value arrow; + + // squish vertically to get an average + area' = resize Kernel_linear 1 (1 / width.value) area; + } + } + } +} + +Extract_arrow_item = class + Menuaction "Extract _Arrow" "extract the area around an arrow" { + action x = class + _value { + _vislevel = 3; + + width = Scale "Width" 1 40 1; + displace = Scale "Horizontal displace" (-50) 50 0; + vdisplace = Scale "Vertical displace" (-50) 50 0; + + _value + = map_unary (extract_arrow + displace.value vdisplace.value width.value) x; + } +} + +Hist_plot_item = class + Menuaction "Plot _Object" + "plot an object as a bar, point or line graph" { + action x = class + _result { + _vislevel = 3; + + caption = Expression "Chart caption" "none"; + format = Option_enum "Format" Plot_format.names "YYYY"; + style = Option_enum "Style" Plot_style.names "Line"; + + auto = Toggle "Auto Range" true; + xmin = Expression "X range minimum" 0; + xmax = Expression "X range maximum" 1; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + xcaption = Expression "X axis caption" "none"; + ycaption = Expression "Y axis caption" "none"; + series_captions = Expression "Series captions" ["Band 0"]; + + _result + = Plot options (image x) + { + options + = [$style => style.value, $format => format.value] ++ + range ++ captions; + range + = [], auto + = [$xmin => xmin.expr, $xmax => xmax.expr, + $ymin => ymin.expr, $ymax => ymax.expr]; + + captions + = concat (map test caption_options) ++ + [$series_captions => series_captions.expr] + { + caption_options = [ + $caption => caption.expr, + $xcaption => xcaption.expr, + $ycaption => ycaption.expr + ]; + test x + = [], value == "none" + = [option_name => value] + { + [option_name, value] = x; + } + } + + image x + = image (extract_arrow 0 0 1 x), is_Arrow x + = get_image x, has_image x + = x2b im, b == 1 + = im + { + im = get_image (to_image x); + w = get_width im; + h = get_height im; + b = get_bands im; + + // matrix to image makes a 1-band mxn image + // we need to put columns into bands + x2b im + = bandjoin (map extract_col [0 .. w - 1]) + { + extract_col x = extract_area x 0 1 h im; + } + } + } + } +} diff --git a/share/nip2/compat/8.5/Image.def b/share/nip2/compat/8.5/Image.def new file mode 100644 index 00000000..59cec551 --- /dev/null +++ b/share/nip2/compat/8.5/Image.def @@ -0,0 +1,2272 @@ +Image_new_item = class Menupullright "_New" "make new things" { + Image_black_item = class Menuaction "_Image" "make a new image" { + format_names = [ + "8-bit unsigned int - UCHAR", // 0 + "8-bit signed int - CHAR", // 1 + "16-bit unsigned int - USHORT", // 2 + "16-bit signed int - SHORT", // 3 + "32-bit unsigned int - UINT", // 4 + "32-bit signed int - INT", // 5 + "32-bit float - FLOAT", // 6 + "64-bit complex - COMPLEX", // 7 + "64-bit float - DOUBLE", // 8 + "128-bit complex - DPCOMPLEX" // 9 + ]; + + action = class + Image _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + nbands = Expression "Image bands" 1; + format_option = Option "Image format" format_names 0; + type_option = Option_enum "Image type" + Image_type.type_names "B_W"; + pixel = Expression "Pixel value" 0; + + _result + = image_new (to_real nwidth) (to_real nheight) (to_real nbands) + (to_real format_option) Image_coding.NOCODING + type_option.value_thing pixel.expr 0 0; + } + } + + Image_new_from_image_item = class + Menuaction "_From Image" "make a new image based on image x" { + action x = class + Image _result { + _vislevel = 3; + + pixel = Expression "Pixel value" 0; + + _result + = image_new x.width x.height x.bands + x.format x.coding x.type pixel.expr x.xoffset x.yoffset; + } + } + + Image_region_item = class + Menupullright "_Region on Image" "make a new region on an image" { + Region_item = class + Menuaction "_Region" "make a region on an image" { + action image = scope.Region_relative image 0.25 0.25 0.5 0.5; + } + + Mark_item = class + Menuaction "_Point" "make a point on an image" { + action image = scope.Mark_relative image 0.5 0.5; + } + + Arrow_item = class + Menuaction "_Arrow" "make an arrow on an image" { + action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; + } + + HGuide_item = class + Menuaction "_Horizontal Guide" + "make a horizontal guide on an image" { + action image = scope.HGuide image 0.5; + } + + VGuide_item = class + Menuaction "_Vertical Guide" "make a vertical guide on an image" { + action image = scope.VGuide image 0.5; + } + + sep1 = Menuseparator; + + Move_item = class + Menuaction "From Region" + "new region on image using existing region as a guide" { + action a b + = map_binary process a b + { + process a b + = x.Region target x.left x.top x.width x.height, + is_Region x + = x.Arrow target x.left x.top x.width x.height, + is_Arrow x + = error "bad arguments to region-from-region" + { + // prefer image then region + compare a b + = false, + !is_Image a && is_Image b + = false, + is_Region a && !is_Region b + = true; + + [target, x] = sortc compare [a, b]; + } + } + } + } +} + +Image_convert_to_image_item = class + Menuaction "Con_vert to Image" "convert anything to an image" { + action x = to_image x; +} + +Image_number_format_item = class + Menupullright "_Format" "convert numeric format" { + + U8_item = class + Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { + action x = map_unary cast_unsigned_char x; + } + + U16_item = class + Menuaction "1_6 bit unsigned" + "convert to unsigned 16 bit [0, 65535]" { + action x = map_unary cast_unsigned_short x; + } + + U32_item = class + Menuaction "_32 bit unsigned" + "convert to unsigned 32 bit [0, 4294967295]" { + action x = map_unary cast_unsigned_int x; + } + + sep1 = Menuseparator; + + S8_item = class + Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { + action x = map_unary cast_signed_char x; + } + + S16_item = class + Menuaction "16 b_it signed" + "convert to signed 16 bit [-32768, 32767]" { + action x = map_unary cast_signed_short x; + } + + S32_item = class + Menuaction "32 bi_t signed" + "convert to signed 32 bit [-2147483648, 2147483647]" { + action x = map_unary cast_signed_int x; + } + + sep2 = Menuseparator; + + Float_item = class + Menuaction "_Single precision float" + "convert to IEEE 32 bit float" { + action x = map_unary cast_float x; + } + + Double_item = class + Menuaction "_Double precision float" + "convert to IEEE 64 bit float" { + action x = map_unary cast_double x; + } + + sep3 = Menuseparator; + + Scmplxitem = class + Menuaction "Single _precision complex" + "convert to 2 x IEEE 32 bit float" { + action x = map_unary cast_complex x; + } + + Dcmplx_item = class + Menuaction "Double p_recision complex" + "convert to 2 x IEEE 64 bit float" { + action x = map_unary cast_double_complex x; + } +} + +Image_header_item = class + Menupullright "_Header" "do stuff to the image header" { + + Image_get_item = class + Menupullright "_Get" "get header fields" { + + // the header fields we can get + fields = class { + type = 0; + width = 1; + height = 2; + format = 3; + bands = 4; + xres = 5; + yres = 6; + xoffset = 7; + yoffset = 8; + coding = 9; + + field_names = Enum [ + $width => width, + $height => height, + $bands => bands, + $format => format, + $type => type, + $xres => xres, + $yres => yres, + $xoffset => xoffset, + $yoffset => yoffset, + $coding => coding + ]; + + field_option name = Option_enum (_ "Field") field_names name; + + field_funcs = Table [ + [type, get_type], + [width, get_width], + [height, get_height], + [format, get_format], + [bands, get_bands], + [xres, get_xres], + [yres, get_yres], + [xoffset, get_xoffset], + [yoffset, get_yoffset], + [coding, get_coding] + ]; + } + + get_field field_name x = class + _result { + _vislevel = 3; + + field = fields.field_option field_name; + + _result + = map_unary (Real @ + fields.field_funcs.lookup 0 1 field.value_thing) x; + } + + Width_item = class + Menuaction "_Width" "get width" { + action x = get_field "width" x; + } + + Height_item = class + Menuaction "_Height" "get height" { + action x = get_field "height" x; + } + + Bands_item = class + Menuaction "_Bands" "get bands" { + action x = get_field "bands" x; + } + + Format_item = class + Menuaction "_Format" "get format" { + action x = get_field "format" x; + } + + Type_item = class + Menuaction "_Type" "get type" { + action x = get_field "type" x; + } + + Xres_item = class + Menuaction "_Xres" "get X resolution" { + action x = get_field "xres" x; + } + + Yres_item = class + Menuaction "_Yres" "get Y resolution" { + action x = get_field "yres" x; + } + + Xoffset_item = class + Menuaction "X_offset" "get X offset" { + action x = get_field "xoffset" x; + } + + Yoffset_item = class + Menuaction "Yo_ffset" "get Y offset" { + action x = get_field "yoffset" x; + } + + Coding_item = class + Menuaction "_Coding" "get coding" { + action x = get_field "coding" x; + } + + sep1 = Menuseparator; + + Custom_item = class + Menuaction "C_ustom" "get any header field" { + action x = class + _result { + _vislevel = 3; + + field = String "Field" "Xsize"; + parse = Option "Parse" [ + "No parsing", + "Parse string as integer", + "Parse string as real", + "Parse string as hh:mm:ss" + ] 0; + + _result + = map_unary (wrap @ process @ get_header field.value) x + { + parse_str parse str = parse (split is_space str)?0; + + parse_field name cast parse x + = cast x, is_number x + = parse_str parse x, is_string x + = error ("not " ++ name); + + get_int = parse_field "int" + cast_signed_int parse_int; + get_float = parse_field "float" + cast_float parse_float; + get_time = parse_field "hh:mm:ss" + cast_signed_int parse_time; + + wrap x + = Real x, is_real x + = Vector x, is_real_list x + = Image x, is_image x + = Bool x, is_bool x + = Matrix x, is_matrix x + = String "String" x, is_string x + = List x, is_list x + = x; + + process = [ + id, + get_int, + get_float, + get_time + ]?parse; + } + } + } + } + + sep1 = Menuseparator; + + Image_set_meta_item = class + Menuaction "_Set" "set image metadata" { + action x = class + _result { + _vislevel = 3; + + fname = String "Field" "field-name"; + val = Expression "Value" 42; + + _result + = map_unary process x + { + process image + = set_header fname.value val.expr image; + } + } + } + + Image_edit_header_item = class + Menuaction "_Edit" "change advisory header fields of image" { + type_names = Image_type.type_names; + all_names = sort (map (extract 0) type_names.value); + + get_prop has get def x + = get x, has x + = def; + + action x = class + _result { + _vislevel = 3; + + nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); + nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); + nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); + nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); + + type_option + = Option_enum "Image type" Image_type.type_names + (Image_type.type_names.get_name type) + { + type + = x.type, is_Image x + = Image_type.MULTIBAND; + } + + _result + = map_unary process x + { + process image + = Image (im_copy_set image.value type_option.value_thing + (to_real nxres) (to_real nyres) + (to_real nxoff) (to_real nyoff)); + } + } + } +} + +Image_cache_item = class + Menuaction "C_ache" "cache calculated image pixels" { + action x = class + _result { + _vislevel = 3; + + tile_width = Number "Tile width" 128; + tile_height = Number "Tile height" 128; + max_tiles = Number "Maximum number of tiles to cache" (-1); + + _result + = map_unary process x + { + process image + = cache (to_real tile_width) (to_real tile_height) + (to_real max_tiles) image; + } + } +} + +#separator + +Image_levels_item = class + Menupullright "_Levels" "change image levels" { + Scale_item = class + Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { + action x = map_unary scale x; + } + + Linear_item = class + Menuaction "_Linear" "linear transform of image levels" { + action x = class + _result { + _vislevel = 3; + + scale = Scale "Scale" 0.001 3 1; + offset = Scale "Offset" (-128) 128 0; + + _result + = map_unary adj x + { + adj x + // only force back to input type if this is a thing + // with a type ... so we work for Colour / Matrix etc. + = clip2fmt x.format x', has_member "format" x + = x' + { + x' = x * scale + offset; + } + } + } + } + + Gamma_item = class + Menuaction "_Power" "power transform of image levels (gamma)" { + action x = class + _result { + _vislevel = 3; + + gamma = Scale "Gamma" 0.001 4 1; + image_maximum_hint = "You may need to change image_maximum if " ++ + "this is not an 8 bit image"; + im_mx + = Expression "Image maximum" mx + { + mx + = Image_format.maxval x.format, has_format x + = 255; + } + + _result + = map_unary gam x + { + gam x + = clip2fmt (get_format x) x', has_format x + = x' + { + x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; + } + } + } + } + + Tone_item = class + Menuaction "_Tone Curve" "adjust tone curve" { + action x = class + _result { + _vislevel = 3; + + b = Scale "Black point" 0 100 0; + w = Scale "White point" 0 100 100; + + sp = Scale "Shadow point" 0.1 0.3 0.2; + mp = Scale "Mid-tone point" 0.4 0.6 0.5; + hp = Scale "Highlight point" 0.7 0.9 0.8; + + sa = Scale "Shadow adjust" (-15) 15 0; + ma = Scale "Mid-tone adjust" (-30) 30 0; + ha = Scale "Highlight adjust" (-15) 15 0; + + curve = tone_build x.format b w sp mp hp sa ma ha; + + _result = map_unary (hist_map curve) x; + } + } +} + +Image_transform_item = class + Menupullright "_Transform" "transform images" { + Rotate_item = class + Menupullright "Ro_tate" "rotate image" { + Fixed_item = class + Menupullright "_Fixed" "clockwise rotation by fixed angles" { + rotate_widget default x = class + _result { + _vislevel = 3; + + angle = Option "Rotate by" [ + "Don't rotate", + "90 degrees clockwise", + "180 degrees", + "90 degrees anticlockwise" + ] default; + + _result + = map_unary process x + { + process = [ + // we can't use id here since we want to "declass" + // the members of x ... consider if x is a crop class, + // for example, we don't want to inherit from crop, we + // want to make a new image class + rot180 @ rot180, + rot90, + rot180, + rot270 + ] ? angle; + } + } + + Rot90_item = class + Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { + action x = rotate_widget 1 x; + } + + Rot180_item = class + Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { + action x = rotate_widget 2 x; + } + + Rot270_item = class + Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { + action x = rotate_widget 3 x; + } + } + + Free_item = class + Menuaction "_Free" "clockwise rotation by any angle" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "Angle" (-180) 180 0; + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary process x + { + process image + = rotate interp angle image; + } + } + } + + Straighten_item = class + Menuaction "_Straighten" + ("smallest rotation that makes an arrow either horizontal " ++ + "or vertical") { + action x = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary straighten x + { + straighten arrow + = rotate interp angle'' arrow.image + { + x = arrow.width; + y = arrow.height; + + angle = im (polar (x, y)); + + angle' + = angle - 360, angle > 315 + = angle - 180, angle > 135 + = angle; + + angle'' + = -angle', angle' >= (-45) && angle' < 45 + = 90 - angle'; + } + } + } + } + } + + Flip_item = class + Menupullright "_Flip" "mirror left/right or up/down" { + Left_right_item = class + Menuaction "_Left Right" "mirror object left/right" { + action x = map_unary fliplr x; + } + + Top_bottom_item = class + Menuaction "_Top Bottom" "mirror object top/bottom" { + action x = map_unary fliptb x; + } + } + + Resize_item = class + Menupullright "_Resize" "change image size" { + Scale_item = class + Menuaction "_Scale" "scale image size by a factor" { + action x = class + _result { + _vislevel = 3; + + xfactor = Expression "Horizontal scale factor" 1; + yfactor = Expression "Vertical scale factor" 1; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + process image + = resize kernel xfactor yfactor image; + } + } + } + + Size_item = class + Menuaction "_Size To" "resize to a fixed size" { + action x = class + _result { + _vislevel = 3; + + which = Option "Resize axis" [ + "Shortest", + "Longest", + "Horizontal", + "Vertical" + ] 0; + size = Expression "Resize to (pixels)" 128; + aspect = Toggle "Break aspect ratio" false; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + process image + = resize kernel h v image, aspect + = resize kernel fac fac image + { + xfac = to_real size / image.width; + yfac = to_real size / image.height; + max_factor + = [xfac, 1], xfac > yfac + = [1, yfac]; + min_factor + = [xfac, 1], xfac < yfac + = [1, yfac]; + [h, v] = [ + max_factor, + min_factor, + [xfac, 1], + [1, yfac]]?which; + + fac + = h, v == 1 + = v; + } + } + } + } + + Size_within_item = class + Menuaction "Size _Within" "size to fit within a rectangle" { + action x = class + _result { + _vislevel = 3; + + // the rects we size to fit within + _rects = [ + [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], + [1280, 1024], [1024, 768], [800, 600], [640, 480] + ]; + + within = Option "Fit within (pixels)" ( + [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ + ["Custom"] + ) 4; + custom_width = Expression "Custom width" 1000; + custom_height = Expression "Custom height" 1000; + size = Option "Page size" [ + "Full page", "Half page", "Quarter page" + ] 0; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + xdiv = [1, 2, 2]?size; + ydiv = [1, 1, 2]?size; + allrect = _rects ++ [ + [custom_width.expr, custom_height.expr] + ]; + [width, height] = allrect?within; + + process x + = resize kernel fac fac x, fac < 1 + = x + { + xfac = (width / xdiv) / x.width; + yfac = (height / ydiv) / x.height; + fac = min_pair xfac yfac; + } + } + } + } + + Resize_canvas_item = class + Menuaction "_Canvas" "change size of surrounding image" { + action x = class + _result { + _vislevel = 3; + + // try to guess a sensible size for the new image + _guess_size + = x.rect, is_Image x + = Rect 0 0 100 100; + + nwidth = Expression "New width (pixels)" _guess_size.width; + nheight = Expression "New height (pixels)" _guess_size.height; + bgcolour = Expression "Background colour" 0; + + position = Option "Position" [ + "North-west", + "North", + "North-east", + "West", + "Centre", + "East", + "South-west", + "South", + "South-east", + "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_unary process x + { + process image + = insert_noexpand xp yp image background + { + width = image.width; + height = image.height; + coding = image.coding; + bands + = 3, coding == Image_coding.LABPACK + = image.bands; + format + = Image_format.FLOAT, coding == Image_coding.LABPACK + = image.format; + type = image.type; + + // placement vectors ... left, centre, right + xposv = [0, to_real nwidth / 2 - width / 2, + to_real nwidth - width]; + yposv = [0, to_real nheight / 2 - height / 2, + to_real nheight - height]; + xp + = left, position == 9 + = xposv?((int) (position % 3)); + yp + = top, position == 9 + = yposv?((int) (position / 3)); + + background = image_new nwidth nheight + bands format coding type bgcolour.expr 0 0; + } + } + } + } + } + + Image_map_item = class + Menuaction "_Map" "map an image through a 2D transform image" { + action a b = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_binary trans a b + { + trans a b + = mapim interp.value in index + { + // get the index image first + [index, in] = sortc (const is_twocomponent) [a, b]; + + // is a two-component image, ie. one band complex, or + // two-band non-complex + is_twocomponent x + = is_nonc x || is_c x; + is_nonc x + = has_bands x && get_bands x == 2 && + has_format x && !is_complex_format (get_format x); + is_c x + = has_bands x && get_bands x == 1 && + has_format x && is_complex_format (get_format x); + is_complex_format f + = f == Image_format.COMPLEX || + f == Image_format.DPCOMPLEX; + } + } + } + } + + Image_perspective_item = Perspective_item; + + Image_rubber_item = class + Menupullright "Ru_bber Sheet" + "automatically warp images to superposition" { + rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; + rubber_order = Option "Order" ["0", "1", "2", "3"] 1; + rubber_wrap = Toggle "Wrap image edges" false; + + // a transform ... a matrix, plus the size of the image the + // matrix was made for + Transform matrix image_width image_height = class + matrix { + // scale a transform ... if it worked for a m by n image, make + // it work for a (m * xfac) by (y * yfac) image + rescale xfac yfac + = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) + (image_width * xfac) (image_height * yfac) + { + facs = [ + [xfac, yfac], + [1, 1], + [1, 1], + [1 / xfac, 1 / yfac], + [1 / xfac, 1 / yfac], + [1 / xfac, 1 / yfac] + ]; + } + } + + // yuk!!!! fix is_instanceof to not need absolute names + is_Transform = is_instanceof + "Image_transform_item.Image_rubber_item.Transform"; + + Find_item = class + Menuaction "_Find" + ("find a transform which will map sample image onto " ++ + "reference") { + action reference sample = class + _trn { + _vislevel = 3; + + // controls + order = rubber_order; + interp = rubber_interp; + wrap = rubber_wrap; + max_err = Expression "Maximum error" 0.3; + max_iter = Expression "Maximum iterations" 10; + + // transform + [sample', trn, err] = transform_search + max_err max_iter order interp wrap + sample reference; + transformed_image = Image sample'; + _trn = Transform trn reference.width reference.height; + final_error = err; + } + } + + Apply_item = class + Menuaction "_Apply" "apply a transform to an image" { + action a b = class + _result { + _vislevel = 3; + + // controls + interp = rubber_interp; + wrap = rubber_wrap; + + _result + = map_binary trans a b + { + trans a b + = transform interp wrap t' i + { + // get the transform arg first + [i, t] = sortc (const is_Transform) [a, b]; + t' = t.rescale (i.width / t.image_width) + (i.height / t.image_height); + } + } + } + } + } + + sep1 = Menuseparator; + + Match_item = class + Menuaction "_Linear Match" + "rotate and scale one image to match another" { + action x y = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + _b = find_image y; + + ap1 = Mark_relative _a 0.5 0.25; + bp1 = Mark_relative _b 0.5 0.25; + ap2 = Mark_relative _a 0.5 0.75; + bp2 = Mark_relative _b 0.5 0.75; + + refine = Toggle "Refine selected tie-points" false; + lock = Toggle "No resize" false; + + _result + = map_binary process x y + { + process a b + = Image b''' + { + _prefs = Workspaces.Preferences; + window = _prefs.MOSAIC_WINDOW_SIZE; + object = _prefs.MOSAIC_OBJECT_SIZE; + + a' = a.value; + b' = b.value; + + b'' = clip2fmt a.format b'; + + // return p2 ... if lock is set, return a p2 a standard + // distance along the vector joining p1 and p2 + norm p1 p2 + = Rect left' top' 0 0, lock + = p2 + { + v = (p2.left - p1.left, p2.top - p1.top); + // 100000 to give precision since we pass points as + // ints to match + n = 100000 * sign v; + left' = p1.left + re n; + top' = p1.top + im n; + } + + ap2'' = norm ap1 ap2; + bp2'' = norm bp1 bp2; + + b''' + = im_match_linear_search a' b'' + ap1.left ap1.top bp1.left bp1.top + ap2''.left ap2''.top bp2''.left bp2''.top + object window, + // we can't search if lock is on + refine && !lock + = im_match_linear a' b'' + ap1.left ap1.top bp1.left bp1.top + ap2''.left ap2''.top bp2''.left bp2''.top; + } + } + } + } + + Image_perspective_match_item = Perspective_match_item; +} + +Image_band_item = class + Menupullright "_Band" "manipulate image bands" { + // like extract_bands, but return [] for zero band image + // makes compose a bit simpler + exb b n x + = [], to_real n == 0 + = extract_bands b n x; + + Extract_item = class Menuaction "_Extract" "extract bands from image" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from band" 0; + number = Expression "Extract this many bands" 1; + + _result = map_unary (exb first number) x; + } + } + + Insert_item = class Menuaction "_Insert" "insert bands into image" { + action x y = class + _result { + _vislevel = 3; + + first = Expression "Insert at position" 0; + + _result + = map_binary process x y + { + process im1 im2 + = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 + { + f = to_real first; + b = im1.bands; + } + } + } + } + + Delete_item = class Menuaction "_Delete" "delete bands from image" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from band" 0; + number = Expression "Delete this many bands" 1; + + _result + = map_unary process x + { + process im + = exb 0 f im ++ exb (f + n) (b - (f + n)) im + { + f = to_real first; + n = to_real number; + b = im.bands; + } + } + } + } + + Bandwise_item = Image_join_item.Bandwise_item; + + sep1a = Menuseparator; + + Bandand_item = class + Menuaction "Bitwise Band AND" "bitwise AND of image bands" { + action x = bandand x; + } + + Bandor_item = class + Menuaction "Bitwise Band OR" "bitwise OR of image bands" { + action x = bandor x; + } + + sep2 = Menuseparator; + + To_dimension_item = class + Menuaction "To D_imension" "convert bands to width or height" { + action x = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + + _result + = map_unary process x + { + process im + = foldl1 [join_lr, join_tb]?orientation (bandsplit im); + } + } + } + + To_bands_item = class + Menuaction "To B_ands" "turn width or height to bands" { + action x = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + + _result + = map_unary process x + { + process im + = bandjoin (map extract_column [0 .. im.width - 1]), + orientation == 0 + = bandjoin (map extract_row [0 .. im.height - 1]) + { + extract_column n + = extract_area n 0 1 im.height im; + extract_row n + = extract_area 0 n im.width 1 im; + } + } + } + } +} + +Image_alpha_item = class + Menupullright "_Alpha" "manipulate image alpha" { + + Add_item = class Menuaction "_Add" "add alpha" { + action x = class + _result { + _vislevel = 3; + + opacity = Expression "Opacity (255 == solid)" 255; + + _result = x ++ to_real opacity; + } + } + + Flatten_item = class Menuaction "_Flatten" "flatten alpha out of image" { + action x = class + _result { + _vislevel = 3; + + bg = Expression "Background" 0; + + _result = map_unary (flattenimage bg) x; + } + } + + Extract_item = class Menuaction "_Extract" "extract alpha" { + action x + = map_unary exb x + { + exb x = extract_bands (x.bands - 1) 1 x; + } + } + + Drop_item = class Menuaction "_Drop" "drop alpha" { + action x + = map_unary exb x + { + exb x = extract_bands 0 (x.bands - 1) x; + } + } + + sep1 = Menuseparator; + + Premultiply_item = class Menuaction "_Premultiply" "premultiply alpha" { + action x = premultiply x; + } + + Unpremultiply_item = class + Menuaction "_Unpremultiply" "unpremultiply alpha" { + action x = unpremultiply x; + } + + sep2 = Menuseparator; + + Blend_alpha_item = Filter_blend_item.Blend_alpha_item; + +} + +Image_crop_item = class + Menuaction "_Crop" "extract a rectangular area from an image" { + action x + = crop x [l, t, w, h] + { + fields = [ + [has_left, get_left, 0], + [has_top, get_top, 0], + [has_width, get_width, 100], + [has_height, get_height, 100] + ]; + + [l, t, w, h] + = map get_default fields + { + get_default line + = get x, has x + = default + { + [has, get, default] = line; + } + } + } + + crop x geo = class + _result { + _vislevel = 3; + + l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); + t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); + w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); + h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); + + _result + = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] + { + extract im l t w h + = extract_area left' top' width' height' im + { + width' = min_pair (to_real w) im.width; + height' = min_pair (to_real h) im.height; + left' = range 0 (to_real l) (im.width - width'); + top' = range 0 (to_real t) (im.height - height'); + } + } + } +} + +Image_insert_item = class + Menuaction "_Insert" "insert a small image into a large image" { + action a b + = insert_position, is_Group a || is_Group b + = insert_area + { + insert_area = class + _result { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _vislevel = 3; + + // sort to get smallest first + _pred x y = x.width * x.height < y.width * y.height; + [_a', _b'] = sortc _pred [a, b]; + + place + = Area _b' left top width height + { + // be careful in case b is smaller than a + left = max_pair 0 ((_b'.width - _a'.width) / 2); + top = max_pair 0 ((_b'.height - _a'.height) / 2); + width = min_pair _a'.width _b'.width; + height = min_pair _a'.height _b'.height; + } + + _result + = insert_noexpand place.left place.top + (clip2fmt _b'.format a'') _b' + { + a'' = extract_area 0 0 place.width place.height _a'; + } + } + + insert_position = class + _result { + _vislevel = 3; + + position = Option "Position" [ + "North-west", + "North", + "North-east", + "West", + "Centre", + "East", + "South-west", + "South", + "South-east", + "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_binary insert a b + { + insert a b + = insert_noexpand left top (clip2fmt b.format a) b, + position == 9 + = insert_noexpand xp yp (clip2fmt b.format a) b + { + xr = b.width - a.width; + yr = b.height - a.height; + xp = [0, xr / 2, xr]?((int) (position % 3)); + yp = [0, yr / 2, yr]?((int) (position / 3)); + } + } + } + } +} + +Image_select_item = Select_item; + +Image_draw_item = class + Menupullright "_Draw" "draw lines, circles, rectangles, floods" { + Line_item = class Menuaction "_Line" "draw line on image" { + action x = class + _result { + _vislevel = 3; + + x1 = Expression "Start x" 0; + y1 = Expression "Start y" 0; + x2 = Expression "End x" 100; + y2 = Expression "End y" 100; + + i = Expression "Ink" [0]; + + _result + = map_unary line x + { + line im + = draw_line x1 y1 x2 y2 i.expr im; + } + } + } + + Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { + action x = class + _result { + _vislevel = 3; + + rx = Expression "Left" 50; + ry = Expression "Top" 50; + rw = Expression "Width" 100; + rh = Expression "Height" 100; + + f = Toggle "Fill" true; + + t = Scale "Line thickness" 1 50 3; + + i = Expression "Ink" [0]; + + _result + = map_unary rect x + { + rect im + = draw_rect_width rx ry rw rh f t i.expr im; + } + } + } + + Circle_item = class Menuaction "_Circle" "draw circle on image" { + action x = class + _result { + _vislevel = 3; + + cx = Expression "Centre x" 100; + cy = Expression "Centre y" 100; + r = Expression "Radius" 50; + + f = Toggle "Fill" true; + + i = Expression "Ink" [0]; + + _result + = map_unary circle x + { + circle im + = draw_circle cx cy r f i.expr im; + } + } + } + + Flood_item = class Menuaction "_Flood" "flood bounded area of image" { + action x = class + _result { + _vislevel = 3; + + sx = Expression "Start x" 0; + sy = Expression "Start y" 0; + + e = Option "Flood while" [ + "Not equal to ink", + "Equal to start point" + ] 0; + + // pick a default ink that won't flood, if we can + i + = Expression "Ink" default_ink + { + default_ink + = [0], ! has_image x + = pixel; + pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); + im = get_image x; + } + + _result + = map_unary flood x + { + flood im + = draw_flood sx sy i.expr im, e == 0 + = draw_flood_blob sx sy i.expr im; + } + } + } + + Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { + action x = class + _result { + _vislevel = 3; + + px = Expression "Left" 50; + py = Expression "Top" 50; + wid = Expression "Width" 100; + thick = Scale "Line thickness" 1 50 3; + text = String "Dimension text" "50μm"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + pos = Option "Position Text" ["Above", "Below"] 1; + vp = Option "Dimension by" [ + "Inner Vertical Edge", + "Centre of Vertical", + "Outer Vertical Edge" + ] 1; + dpi = Expression "DPI" 100; + ink = Colour "Lab" [50,0,0]; + + _result + = map_unary process x + { + process im + = blend (Image scale) ink' im + { + // make an ink compatible with the image + ink' = colour_transform_to (get_type im) ink; + + x = to_real px; + y = to_real py; + w = to_real wid; + d = to_real dpi; + + t = floor thick; + + bg = image_new (get_width im) (get_height im) (get_bands im) + (get_format im) (get_coding im) (get_type im) 0 0 0; + draw_block x y w t im = + draw_rect_width x y w t true 1 [255] im; + label = im_text text.value font.value w 1 d; + lw = get_width label; + lh = get_height label; + ly = [y - lh - t, y + 2 * t]?pos; + vx = [ + [x - t, x + w], + [x - t / 2, x + w - t / 2], + [x, x + w - t] + ]?vp; + + scale = (draw_block x y w t @ + draw_block vx?0 (y - 2 * t) t (t * 5) @ + draw_block vx?1 (y - 2 * t) t (t * 5) @ + insert_noexpand (x + w / 2 - lw / 2) ly label) + bg; + } + } + } + } +} + +Image_join_item = class + Menupullright "_Join" "join two or more images together" { + Bandwise_item = class + Menuaction "_Bandwise Join" "join two images bandwise" { + action a b = join a b; + } + + sep1 = Menuseparator; + + join_lr shim bg align a b + = im2 + { + w = a.width + b.width + shim; + h = max_pair a.height b.height; + + back = image_new w h a.bands a.format a.coding a.type bg 0 0; + + ya = [0, max_pair 0 ((b.height - a.height)/2), + max_pair 0 (b.height - a.height)]; + yb = [0, max_pair 0 ((a.height - b.height)/2), + max_pair 0 (a.height - b.height)]; + + im1 = insert_noexpand 0 ya?align a back; + im2 = insert_noexpand (a.width + shim) yb?align b im1; + } + + join_tb shim bg align a b + = im2 + { + w = max_pair a.width b.width; + h = a.height + b.height + shim; + + back = image_new w h a.bands a.format a.coding a.type bg 0 0; + + xa = [0, max_pair 0 ((b.width - a.width)/2), + max_pair 0 (b.width - a.width)]; + xb = [0, max_pair 0 ((a.width - b.width)/2), + max_pair 0 (a.width - b.width)]; + + im1 = insert_noexpand xa?align 0 a back; + im2 = insert_noexpand xb?align (a.height + shim) b im1; + } + + halign_names = ["Top", "Centre", "Bottom"]; + valign_names = ["Left", "Centre", "Right"]; + + Left_right_item = class + Menuaction "_Left to Right" "join two images left-right" { + action a b = class + _result { + _vislevel = 3; + + shim = Scale "Spacing" 0 100 0; + bg_colour = Expression "Background colour" 0; + align = Option "Alignment" halign_names 1; + + _result = map_binary + (join_lr shim.value bg_colour.expr align.value) a b; + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "join two images top-bottom" { + action a b = class + _result { + _vislevel = 3; + + shim = Scale "Spacing" 0 100 0; + bg_colour = Expression "Background colour" 0; + align = Option "Alignment" valign_names 1; + + _result = map_binary + (join_tb shim.value bg_colour.expr align.value) a b; + } + } + + sep2 = Menuseparator; + + Array_item = class + Menuaction "_Array" + "join a list of lists of images into a single image" { + action x = class + _result { + _vislevel = 3; + + hshim = Scale "Horizontal spacing" (-100) (100) 0; + vshim = Scale "Vertical spacing" (-100) (100) 0; + bg_colour = Expression "Background colour" 0; + halign = Option "Horizontal alignment" valign_names 1; + valign = Option "Vertical alignment" halign_names 1; + + // we can't use map_unary since chop-into-tiles returns a group of + // groups and we want to be able to reassemble that + // TODO: chop-into-tiles should return an array class which + // displays as group but does not have the looping behaviour? + _result + = (image_set_origin 0 0 @ + foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ + map (foldl1 (join_lr hshim.value + bg_colour.expr valign.value))) (to_list (to_list x)); + } + } + + ArrayFL_item = class + Menuaction "_Array from List" + "join a list of images into a single image" { + action x = class + _result { + _vislevel = 3; + + ncol = Number "Max. Number of Columns" 1; + hshim = Scale "Horizontal spacing" (-100) (100) 0; + vshim = Scale "Vertical spacing" (-100) (100) 0; + bg_colour = Expression "Background colour" 0; + halign = Option "Horizontal alignment" valign_names 1; + valign = Option "Vertical alignment" halign_names 1; + snake = Toggle "Reverse the order of every other row" false; + + _l + = split_lines ncol.value x.value, is_Group x + = split_lines ncol.value x; + + _l' + = map2 reverse_if_odd [0..] _l, snake + = _l + { + reverse_if_odd n x + = reverse x, n % 2 == 1 + = x; + } + + _result + = (image_set_origin 0 0 @ + foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ + map (foldl1 (join_lr hshim.value + bg_colour.expr valign.value))) (to_list (to_list _l')); + } + } +} + +Image_tile_item = class + Menupullright "Til_e" "tile an image across and down" { + tile_widget default_type x = class + _result { + _vislevel = 3; + + across = Expression "Tiles across" 2; + down = Expression "Tiles down" 2; + repeat = Option "Tile type" + ["Replicate", "Four-way mirror"] default_type; + + _result + = map_unary process x + { + process image + = tile across down image, repeat == 0 + = tile across down image'' + { + image' = insert image.width 0 (fliplr image) image; + image'' = insert 0 image.height (fliptb image') image'; + } + } + } + + Replicate_item = class + Menuaction "_Replicate" "replicate image across and down" { + action x = tile_widget 0 x; + } + + Fourway_item = class + Menuaction "_Four-way Mirror" "four-way mirror across and down" { + action x = tile_widget 1 x; + } + + Chop_item = class + Menuaction "_Chop Into Tiles" "slice an image into tiles" { + action x = class + _result { + _vislevel = 3; + + tile_width = Expression "Tile width" 100; + tile_height = Expression "Tile height" 100; + hoverlap = Expression "Horizontal overlap" 0; + voverlap = Expression "Vertical overlap" 0; + + _result + = map_unary (Group @ map Group @ process) x + { + process x + = imagearray_chop tile_width tile_height + hoverlap voverlap x; + } + } + } +} + +#separator + +Pattern_images_item = class + Menupullright "_Patterns" "make a variety of useful patterns" { + Grey_item = class + Menuaction "Grey _Ramp" "make a smooth grey ramp" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + foption = Option "Format" ["8 bit", "float"] 0; + + _result + = Image im + { + gen + = im_grey, foption == 0 + = im_fgrey; + w = to_real nwidth; + h = to_real nheight; + im + = gen w h, orientation == 0 + = rot90 (gen h w); + } + } + } + + Xy_item = class + Menuaction "_XY Image" + "make a two band image whose pixel values are their coordinates" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + + _result = Image (make_xy nwidth nheight); + } + } + + Noise_item = class + Menupullright "_Noise" "various noise generators" { + Gaussian_item = class + Menuaction "_Gaussian" "make an image of gaussian noise" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + mean = Scale "Mean" 0 255 128; + deviation = Scale "Deviation" 0 128 50; + + _result = Image (gaussnoise nwidth nheight + mean.value deviation.value); + } + } + + Fractal_item = class + Menuaction "_Fractal" "make a fractal noise image" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + dimension = Scale "Dimension" 2.001 2.999 2.001; + + _result = Image (im_fractsurf (to_real nsize) dimension.value); + } + } + + Perlin_item = class + Menuaction "_Perlin" "Perlin noise image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + cell_size = Expression "Cell size (pixels)" 8; + eight = Toggle "Eight bit output" true; + + _result + = 128 * im + 128, eight + = im + { + im = perlin cell_size nwidth nheight; + } + } + } + + Worley_item = class + Menuaction "_Worley" "Worley noise image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 512; + nheight = Expression "Image height (pixels)" 512; + cell_size = Expression "Cell size (pixels)" 256; + + _result + = worley cell_size nwidth nheight; + } + } + + } + + Checkerboard_item = class + Menuaction "_Checkerboard" "make a checkerboard image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + hpsize = Expression "Horizontal patch size" 8; + vpsize = Expression "Vertical patch size" 8; + hpoffset = Expression "Horizontal patch offset" 0; + vpoffset = Expression "Vertical patch offset" 0; + + _result + = Image (xstripes ^ ystripes) + { + pixels = make_xy nwidth nheight; + xpixels = pixels?0 + to_real hpoffset; + ypixels = pixels?1 + to_real vpoffset; + + make_stripe pix swidth = pix % (swidth * 2) >= swidth; + + xstripes = make_stripe xpixels (to_real hpsize); + ystripes = make_stripe ypixels (to_real vpsize); + } + } + } + + Grid_item = class + Menuaction "Gri_d" "make a grid" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + hspace = Expression "Horizontal line spacing" 8; + vspace = Expression "Vertical line spacing" 8; + thick = Expression "Line thickness" 1; + hoff = Expression "Horizontal grid offset" 4; + voff = Expression "Vertical grid offset" 4; + + _result + = Image (xstripes | ystripes) + { + pixels = make_xy nwidth nheight; + xpixels = pixels?0 + to_real hoff; + ypixels = pixels?1 + to_real voff; + + make_stripe pix swidth = pix % swidth < to_real thick; + + xstripes = make_stripe xpixels (to_real hspace); + ystripes = make_stripe ypixels (to_real vspace); + } + } + } + + Text_item = class + Menuaction "_Text" "make a bitmap of some text" { + action = class + _result { + _vislevel = 3; + + text = String "Text to paint" "Hello world!"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + wrap = Expression "Wrap text at" 500; + align = Option "Alignment" [ + "Left", + "Centre", + "Right" + ] 0; + dpi = Expression "DPI" 300; + + _result = Image (im_text text.value font.value + (to_real wrap) align.value (to_real dpi)); + } + } + + New_CIELAB_slice_item = class + Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + L = Scale "L value" 0 100 50; + + _result = Image (lab_slice (to_real nsize) L.value); + } + } + + sense_option = Option "Sense" [ + "Pass", + "Reject" + ] 0; + + build fn size + = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) + (im_create_fmask size size); + + New_ideal_item = class + Menupullright "_Ideal Fourier Mask" + "make various ideal Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f sense.value fc.value 0 0 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 6) fc.value rw.value 0 0 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 12) fcx.value fcy.value + r.value 0 0; + } + } + } + } + + New_gaussian_item = class + Menupullright "_Gaussian Fourier Mask" + "make various Gaussian Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 4) fc.value ac.value 0 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 10) fc.value rw.value + ac.value 0 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 16) fcx.value fcy.value + r.value ac.value 0; + } + } + } + } + + New_butterworth_item = class + Menupullright "_Butterworth Fourier Mask" + "make various Butterworth Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 2) order.value fc.value + ac.value 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 8) order.value fc.value + rw.value ac.value 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 14) order.value fcx.value + fcy.value r.value ac.value; + } + } + } + } +} + +Test_images_item = class + Menupullright "Test I_mages" "make a variety of test images" { + Eye_item = class + Menuaction "_Spatial Response" + "image for testing the eye's spatial response" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + factor = Scale "Factor" 0.001 1 0.2; + + _result = Image (im_eye (to_real nwidth) (to_real nheight) + factor.value); + } + } + + Zone_plate = class + Menuaction "_Zone Plate" "make a zone plate" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + + _result = Image (im_zone (to_real nsize)); + } + } + + Frequency_test_chart_item = class + Menuaction "_Frequency Testchart" + "make a black/white frequency test pattern" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + sheight = Expression "Strip height (pixels)" 10; + waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; + + _result + = imagearray_assemble 0 0 (transpose [strips]) + { + freq_slice wave = Image (sin (grey / wave) > 0); + strips = map freq_slice waves.expr; + grey = im_fgrey (to_real nwidth) (to_real sheight) * + 360 * (to_real nwidth); + } + } + } + + CRT_test_chart_item = class + Menuaction "CRT _Phosphor Chart" + "make an image for measuring phosphor colours" { + action = class + _result { + _vislevel = 3; + + brightness = Scale "Brightness" 0 255 200; + psize = Expression "Patch size (pixels)" 32; + + _result + = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) + { + + black = image_new (to_real psize) (to_real psize) 1 + Image_format.FLOAT Image_coding.NOCODING + Image_type.B_W 0 0 0; + notblack = black + brightness; + + green = black ++ notblack ++ black; + red = notblack ++ black ++ black; + blue = black ++ black ++ notblack; + white = notblack ++ notblack ++ notblack; + } + } + } + + Greyscale_chart_item = class + Menuaction "_Greyscale" "make a greyscale" { + action = class + _result { + _vislevel = 3; + + pwidth = Expression "Patch width" 8; + pheight = Expression "Patch height" 8; + npatches = Expression "Number of patches" 16; + + _result + = Image (image_set_type Image_type.B_W + (clip2fmt Image_format.UCHAR wedge)) + { + wedge + = 255 / (to_real npatches - 1) * + (int) (strip?0 / to_real pwidth) + { + strip = make_xy (to_real pwidth * to_real npatches) pheight; + } + } + } + } + + CMYK_test_chart_item = class + Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { + action = class + _result { + _vislevel = 3; + + pwidth = Expression "Patch width" 8; + pheight = Expression "Patch height" 8; + npatches = Expression "Number of patches" 16; + + _result + = Image (image_set_type Image_type.CMYK + (clip2fmt Image_format.UCHAR strips)) + { + wedge + = 255 / (to_real npatches - 1) * + (int) (strip?0 / to_real pwidth) + { + strip = make_xy (to_real pwidth * to_real npatches) pheight; + } + + black = wedge * 0; + + C = wedge ++ black ++ black ++ black; + M = black ++ wedge ++ black ++ black; + Y = black ++ black ++ wedge ++ black; + K = black ++ black ++ black ++ wedge; + + strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; + } + } + } + + Colour_atlas_item = class + Menuaction "_Colour Atlas" + "make a grid of patches grouped around a colour" { + action = class + _result { + _vislevel = 3; + + start = Colour_picker "Lab" [50,0,0]; + nstep = Expression "Number of steps" 9; + ssize = Expression "Step size" 10; + psize = Expression "Patch size" 32; + sepsize = Expression "Separator size" 4; + + _result + = colour_transform_to (get_type start) blocks''' + { + size = (to_real nstep * 2 + 1) * to_real psize - + to_real sepsize; + xy = make_xy size size; + + xy_grid = (xy % to_real psize) < + (to_real psize - to_real sepsize); + grid = xy_grid?0 & xy_grid?1; + + blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); + lab_start = colour_transform_to Image_type.LAB start; + blocks' = blocks - to_real nstep * to_real ssize + + Vector (tl lab_start.value); + blocks'' = hd lab_start.value ++ Image blocks'; + blocks''' + = image_set_type Image_type.LAB blocks'', Image grid + = 0; + } + } + } +} + diff --git a/share/nip2/compat/8.5/Magick.def b/share/nip2/compat/8.5/Magick.def new file mode 100644 index 00000000..ab6cdb35 --- /dev/null +++ b/share/nip2/compat/8.5/Magick.def @@ -0,0 +1,2423 @@ +/* + + ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). + 1-Apr-2014 + Minor corrections to Geometry_widget and Alpha. + Added loads of widgets and Menuactions. + Not fully tested. + 5-Apr-2014 + Many more menu actions. + Reorganised Magick menu. + 10-Apr-2014 + Many more menu actions. + 11-Apr-2014 jcupitt + Split to separate Magick.def + 13-Apr-2014 snibgo + Put "new image" items into sub-menu. + New class VirtualPixlBack. + 17-Apr-2014 snibgo + Many small changes. + A few new menu options. + Created sub-menu for multi-input operations. + 3-May-2014 jcupitt + Put quotes around ( in shadow to help unix + + Last update: 17-Apr-2014. + + For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. +*/ + +// We don't need Noop. +/*=== +Magick_noop_item = class + Menuaction "_Noop" "no operation" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} +===*/ + +Magick_testPar_item = class + Menuaction "_TestPar" "no operation" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "( +clone ) +append ", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +/* Removed Read_item and Write_item, much better to use nip2 load/save image. + * Plus they can load all libMagick formats anyway. + */ + + +// Put "new image" items into sub-menu +Magick_NewImageMenu_item = class + Menupullright "_New image" "make a new image" { + + Magick_newcanvas_item = class + Menuaction "_Solid colour" "make image of solid colour" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + colour = Magick.generalcol_widget; + command = Magick.command [ + size._flag, + "\"canvas:" ++ colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_builtin_item = class + Menuaction "_Built-in image" "create a built-in image" { + action = class + _result { + _vislevel = 3; + + builtin = Magick.builtin_widget; + command = Magick.command [ + builtin._flag, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_gradient_item = class + Menuaction "_Gradient" "make a linear gradient between two colours" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + topColour = Magick.generalcol_widget; + bottomColour = Magick.generalcol_widget; + + command = Magick.command [ + size._flag, + concat ["\"gradient:", + topColour._flag, "-", bottomColour._flag, "\""], + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_hald_item = class + Menuaction "_Hald-clut image" "create an identity hald-clut image" { + action = class + _result { + _vislevel = 3; + + order = Expression "order" 8; + command = Magick.command [ + "hald:" ++ print order.expr, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_pattern_item = class + Menuaction "_Pattern" "create pattern" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + pattern = Magick.pattern_widget; + command = Magick.command [ + size._flag, + pattern._flag, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_plasma_item = class + Menuaction "_Plasma image" "create plasma image" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + // FIXME? ColourA-ColourB. + // FIXME? Allow plasma:fractal? + + command = Magick.command [ + size._flag, + "plasma:", + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_radialgradient_item = class + Menuaction "_Radial gradient" + "make a radial gradient between two colours" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + innerColour = Magick.generalcol_widget; + outerColour = Magick.generalcol_widget; + + command = Magick.command [ + size._flag, + concat ["\"radial-gradient:", + innerColour._flag, "-", outerColour._flag, "\""], + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } +} // end Magick_NewImageMenu_item + + +Magick_MultiMenu_item = class + Menupullright "_Multiple inputs" "make an image from multiple images" { + + Magick_composite_item = class + Menuaction "_Composite" "composite two images (without mask)" { + action x y = class + _result { + _vislevel = 3; + + method = Magick.compose_widget; + offsets = Magick.OffsetGeometry_widget; + gravity = Magick.gravity_widget; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-geometry", offsets._flag, + gravity._flag, + method._flag, + "-composite", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + Magick_compositeMask_item = class + Menuaction "_Composite masked" "composite two images (with mask)" { + action x y z = class + _result { + _vislevel = 3; + + method = Magick.compose_widget; + offsets = Magick.OffsetGeometry_widget; + gravity = Magick.gravity_widget; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "\"%s\"", + "-geometry", offsets._flag, + gravity._flag, + method._flag, + "-composite", + "\"%s\"" + ]; + + _result = Magick.system3 command x y z; + } + } + + // FIXME: other operations like remap that take another image as arguments are: + // mask (pointless?), texture, tile (pointless?) + + // FIXME: operations that take a filename that isn't an image: + // cdl, profile + + Magick_clut_item = class + Menuaction "_Clut" "replace values using second image as colour look-up table" { + action x y = class + _result { + _vislevel = 3; + + // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-clut", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + Magick_haldclut_item = class + Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { + action x y = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-hald-clut", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + + // Encipher and decipher: key files can be text or image files. + + Magick_encipher_item = class + Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { + action x = class + _result { + _vislevel = 3; + + pathname = Pathname "Read key file" ""; + isDecipher = Toggle "Decipher" false; + + command = Magick.command [ + "\"%s\"", + if pathname.value == "" then "" else ( + ( if isDecipher then "-decipher " else "-encipher ") ++ + ( "\"" ++ pathname.value ++ "\"" ) + ), + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_profile_item = class + Menuaction "_Profile" "assigns/applies an ICC profile" { + action x = class + _result { + _vislevel = 3; + + pathname1 = Pathname "Read profile file" ""; + pathname2 = Pathname "Read profile file" ""; + + command = Magick.command [ + "\"%s\"", + if pathname1.value == "" then "" else ( + "-profile " ++ + "\"" ++ pathname1.value ++ "\""), + if pathname2.value == "" then "" else ( + "-profile " ++ + "\"" ++ pathname2.value ++ "\""), + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_remap_item = class + Menuaction "_Remap" "reduce colours to those in another image" { + action x y = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-remap", + "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + +} // end Magick_MultiMenu_item + + +Magick_image_type_item = class + Menuaction "_Image Type" "change image type" { + action x = class + _result { + _vislevel = 3; + + imagetype = Magick.imagetype_widget; + + command = Magick.command [ + "\"%s\"", + imagetype._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +sep2 = Menuseparator; + +Magick_alpha_item = class + Menuaction "_Alpha" "add/remove alpha channel" { + action x = class + _result { + _vislevel = 3; + + alpha = Magick.alpha_widget; + command = Magick.command [ + "\"%s\"", + alpha._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_annotate_item = class + Menuaction "_Annotate" "add text annotation" { + action x = class + _result { + _vislevel = 3; + + text = Magick.text_widget; + font = Magick.Font_widget; + geometry = Magick.AnnotGeometry_widget; + gravity = Magick.gravity_widget; + foreground = Magick.foreground_widget; + undercol = Magick.undercol_widget; + antialias = Magick.antialias_widget; + command = Magick.command [ + "\"%s\"", + font._flag, + antialias._flag, + gravity._flag, + foreground._flag, + undercol._flag, + "-annotate", + geometry._flag, + "\"" ++ text.value ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_autoGamma_item = class + Menuaction "_AutoGamma" "automatic gamma" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + command = Magick.command [ + "\"%s\"", + channels._flag, + "-auto-gamma", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_autoLevel_item = class + Menuaction "_AutoLevel" "automatic level" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + command = Magick.command [ + "\"%s\"", + channels._flag, + "-auto-level", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_blurSharpMenu_item = class + Menupullright "_Blur/Sharpen" "blur and sharpen" { + + Magick_adaptive_blur_item = class + Menuaction "_Adaptive Blur" "blur less near edges" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + // note: adaptive-blur doesn't regard VP. + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-adaptive-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_blur_item = class + Menuaction "_Blur" "blur" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_gaussianBlur_item = class + Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-gaussian-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_motionBlur_item = class + Menuaction "_Motion Blur" "simulate motion blur" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + angle = Scale "angle" (-360) 360 0; + channels = Magick.ch_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-motion-blur", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_rotationalBlur_item = class + Menuaction "_RotationalBlur" "blur around the centre" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + angle = Scale "angle (degrees)" (-360) 360 20; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-radial-blur", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_selectiveBlur_item = class + Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + intensity._flag, + virtpixback._flag, + channels._flag, + "-selective-blur", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print threshold.value ++ "%%", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + sep1 = Menuseparator; + + Magick_adaptive_sharpen_item = class + Menuaction "_Adaptive Sharpen" "sharpen more near edges" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-adaptive-sharpen", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_sharpen_item = class + Menuaction "_Sharpen" "sharpen" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-sharpen", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_unsharpen_item = class + Menuaction "_Unsharp" "sharpen with unsharp mask" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + gain = Scale "Gain" (-10) 10 1; + threshold = Scale "Threshold" 0 1 0.05; + virtpixback = Magick.VirtualPixelBack_widget; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-unsharp", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print gain.value ++ "+" ++ + print threshold.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end BlurSharpMenu_item + + +Magick_border_item = class + Menuaction "_Border" "add border of given colour" { + action x = class + _result { + _vislevel = 3; + + compose = Magick.compose_widget; + width = Expression "Width" 3; + bordercol = Magick.bordercol_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + bordercol._flag, + "-border", + print width.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_brightCont_item = class + Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { + action x = class + _result { + _vislevel = 3; + + bri = Scale "brightness" (-100) 100 0; + con = Scale "contrast" (-100) 100 0; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-brightness-contrast", + print bri.value ++ "x" ++ print con.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// Note: canny requires ImageMagick 6.8.9-0 or later. + +Magick_canny_item = class + Menuaction "_Canny" "detect a wide range of edges" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + lowPc = Scale "lower percent" 0 100 10; + highPc = Scale "lower percent" 0 100 10; + + command = Magick.command [ + "\"%s\"", + "-canny", + concat ["\"", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print lowPc.value ++ "%%+" ++ + print highPc.value ++ "%%" ++ "\"" + ], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + +Magick_charcoal_item = class + Menuaction "_Charcoal" "simulate a charcoal drawing" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + factor = Scale "factor" 0 50 1; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-charcoal", + print factor.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_chop_item = class + Menuaction "_Chop" "remove pixels from the interior" { + action x = class + _result { + _vislevel = 3; + + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + gravity._flag, + "-chop", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colorize_item = class + Menuaction "_Colorize" "colorize by given amount" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + val = Scale "value" 0 100 100; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-colorize", + print val.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colors_item = class + Menuaction "_Colors" "reduce number of colors" { + action x = class + _result { + _vislevel = 3; + + treedepth = Expression "Treedepth" 8; + dither = Magick.dither_widget; + quantize = Magick.colorspace_widget; + colors = Expression "Colours" 3; + + command = Magick.command [ + "\"%s\"", + "-quantize", quantize._flag, + "-treedepth", + print treedepth.expr, + dither._flag, + "-colors", + print colors.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: color-matrix? + +Magick_colorspace_item = class + Menuaction "_Colourspace" "convert to arbitrary colourspace" { + action x = class + _result { + _vislevel = 3; + + colsp = Magick.colorspace_widget; + + command = Magick.command [ + "\"%s\"", + "-colorspace", + colsp._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colorspaceGray_item = class + Menuaction "_Colourspace gray" "convert to gray using given intensity method" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-colorspace gray", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_contrast_item = class + Menuaction "_Contrast" "increase or reduce the contrast" { + action x = class + _result { + _vislevel = 3; + + isReduce = Toggle "reduce contrast" false; + + command = Magick.command [ + "\"%s\"", + (if isReduce then "+" else "-") ++ "contrast", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_contrastStretch_item = class + Menuaction "_Contrast stretch" "stretches tones, making some black/white" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + blk = Scale "percent to make black" 0 100 0; + wht = Scale "percent to make white" 0 100 0; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-contrast-stretch", + "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: convolve (bias, kernel) + +Magick_crop_item = class + Menuaction "_Crop" "cut out a rectangular region" { + action x = class + _result { + _vislevel = 3; + + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + gravity._flag, + "-crop", + geometry._flag, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_deskew_item = class + Menuaction "_Deskew" "straighten the image" { + action x = class + _result { + _vislevel = 3; + + threshold = Scale "Threshold (percent)" 0 100 80; + + // FIXME: toggle auto-crop? + + command = Magick.command [ + "\"%s\"", + "-deskew", + "\"" ++ print threshold.value ++ "%%\"", + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_despeckle_item = class + Menuaction "_Despeckle" "reduce the speckles" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-despeckle", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_distort_item = class + Menuaction "_Distort" "distort using a method and arguments" { + action x = class + _result { + _vislevel = 3; + + virtpixback = Magick.VirtualPixelBack_widget; + distort = Magick.distort_widget; + args = String "Arguments" "1,0"; + isPlus = Toggle "Extend to show entire image" false; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + (if isPlus then "+" else "-") ++ "distort", + distort._flag, + args.value, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_draw_item = class + Menuaction "_Draw" "annotate with one or more graphic primitives" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-draw", + concat ["\"", args.value, "\""], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_edge_item = class + Menuaction "_Edge" "detect edges" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "Radius" 3; + + command = Magick.command [ + "\"%s\"", + "-edge", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_emboss_item = class + Menuaction "_Emboss" "emboss" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "Radius" 3; + + command = Magick.command [ + "\"%s\"", + "-emboss", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_enhance_item = class + Menuaction "_Enhance" "enhance a noisy image" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-enhance", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_equalize_item = class + Menuaction "_Equalize" "equalize the histogram" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-equalize", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_evaluate_item = class + Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + operation = Magick.evaluate_widget; + val = Expression "value" 5; + isPc = Toggle "Value is percent" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + operation._flag, + print val.expr ++ if isPc then "%%" else "", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_extent_item = class + Menuaction "_Extent" "set the image size and offset" { + action x = class + _result { + _vislevel = 3; + + background = Magick.background_widget; + compose = Magick.compose_widget; + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + background._flag, + gravity._flag, + "-extent", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + +Magick_FlipFlopMenu_item = class + Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { + + Magick_flip_item = class + Menuaction "_Flip vertically" "mirror upside-down" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-flip", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_flop_item = class + Menuaction "_Flop horizontally" "mirror left-right" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-flop", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_transpose_item = class + Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-transpose +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_transverse_item = class + Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-transverse +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end Magick_FlipFlopMenu_item + + +Magick_floodfill_item = class + Menuaction "_Floodfill" "recolour neighbours that match" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + fuzz = Magick.fuzz_widget; + coordinate = Magick.coordinate_widget; + + // -draw "color x,y floodfill" + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-fuzz", + "\"" ++ print fuzz.value ++ "%%\"", + "-draw \" color", + coordinate._flag, + "floodfill \"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_frame_item = class + Menuaction "_Frame" "surround with border or beveled frame" { + action x = class + _result { + _vislevel = 3; + + border = Magick.bordercol_widget; + compose = Magick.compose_widget; + matte = Magick.mattecol_widget; + geometry = Magick.FrameGeometry_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + border._flag, + matte._flag, + "-frame", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_function_item = class + Menuaction "_Function" "evaluate a function on each pixel channel" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + function = Magick.function_widget; + // FIXME: explain values; use sensible defaults. + values = String "values" "0,0,0,0"; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-function", + function._flag, + values.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_fx_item = class + Menuaction "_Fx" "apply a mathematical expression" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + interpolate = Magick.interpolate_widget; + virtpixback = Magick.VirtualPixelBack_widget; + args = String "Expression" "u*1/2"; + + command = Magick.command [ + "\"%s\"", + channels._flag, + interpolate._flag, + virtpixback._flag, + "-fx", + concat ["\"", args.value, "\""], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gamma_item = class + Menuaction "_Gamma" "apply a gamma correction" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + gamma = Magick.gamma_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-gamma", + print gamma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gradient_item = class + Menuaction "_Gradient" "apply a linear gradient" { + action x = class + _result { + _vislevel = 3; + + colourA = Magick.generalcol_widget; + colourB = Magick.generalcol_widget; + + position = Option "colourA is at" [ + "top", "bottom", + "left", "right", + "top-left", "top-right", + "bottom-left", "bottom-right"] 0; + _baryArg + = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 + = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 + = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 + = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 + = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 + = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 + = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 + = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 + = "dunno"; + + command = Magick.command [ + "\"%s\"", + "-sparse-color barycentric \"" ++ _baryArg ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gradientCorn_item = class + Menuaction "_Gradient corners" + "apply a bilinear gradient between the corners" { + action x = class + _result { + _vislevel = 3; + + colour_top_left = Magick.generalcol_widget; + colour_top_right = Magick.generalcol_widget; + colour_bottom_left = Magick.generalcol_widget; + colour_bottom_right = Magick.generalcol_widget; + + command = Magick.command [ + "\"%s\"", + "-sparse-color bilinear \"" ++ + "0,0," ++ colour_top_left._flag ++ + ",%%[fx:w-1],0" ++ colour_top_right._flag ++ + ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ + ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", + "+depth", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_histogram_item = class + Menuaction "_Histogram" "make a histogram image" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-define histogram:unique-colors=false histogram:" ++ + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_implode_item = class + Menuaction "_Implode" "implode pixels about the center" { + action x = class + _result { + _vislevel = 3; + + factor = Scale "factor" 0 20 1; + interpolate = Magick.interpolate_widget; + // FIXME: virtual-pixel? + + command = Magick.command [ + "\"%s\"", + interpolate._flag, + "-implode", + print factor.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_level_item = class + Menuaction "_Level" "adjust the level of channels" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + blk = Scale "black point" (-100) 200 0; + wht = Scale "white point" (-100) 200 100; + gam = Scale "gamma" 0 30 1; + isPc = Toggle "Levels are percent" true; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "level", + "\"" ++ print blk.value ++ "," ++ + print wht.value ++ (if isPc then "%%" else "") ++ "," ++ + print gam.value ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_levelCols_item = class + Menuaction "_Level colors" "adjust levels to given colours" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + colour_black = Magick.generalcol_widget; + colour_white = Magick.generalcol_widget; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "level-colors", + "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_linearStretch_item = class + Menuaction "_Linear stretch" "stretches tones, making some black/white" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + blk = Scale "percent to make black" 0 100 0; + wht = Scale "percent to make white" 0 100 0; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-linear-stretch", + "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_magnify_item = class + Menuaction "_Magnify" "double the size of the image with pixel art scaling" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-magnify", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_modulate_item = class + Menuaction "_Modulate" "modulate brightness, saturation and hue" { + action x = class + _result { + _vislevel = 3; + + modcolsp = Magick.ModColSp_widget; + bright = Scale "brightness" 0 200 100; + sat = Scale "saturation" 0 200 100; + hue = Scale "hue" 0 200 100; + + command = Magick.command [ + "\"%s\"", + modcolsp._flag, + "-modulate", + print bright.value ++ "," ++ print sat.value ++ "," ++ + print hue.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_monochrome_item = class + Menuaction "_Monochrome" "transform to black and white" { + action x = class + _result { + _vislevel = 3; + + // FIXME: also intensity? + + intensity = Magick.intensity_widget; + treedepth = Expression "Treedepth" 8; + dither = Magick.dither_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + dither._flag, + "-treedepth", + print treedepth.expr, + "-monochrome", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_morphology_item = class + // See http://www.imagemagick.org/Usage/morphology/ + Menuaction "_Morphology" "apply a morphological method" { + action x = class + _result { + _vislevel = 3; + + method = Magick.morphmeth_widget; + iter = Expression "Iterations (-1=repeat until done)" 1; + + kernel = Magick.kernel_widget; + // FIXME: custom kernel eg "3x1+2+0:1,0,0" + // width x height + offsx + offsy : {w*h values} + // each value is 0.0 to 1.0 or "NaN" or "-" + + // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 + // but + // ring takes: radius1, radius2, scale + // rectangle and comet take: width x height + offsx + offsy + // blur takes: radius x sigma + // FIXME: for now, simply allow any string input. + kernel_arg = String "Kernel arguments" ""; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-morphology", + method._flag ++ ":" ++ print iter.expr, + kernel._flag ++ + (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_negate_item = class + Menuaction "_Negate" "negate" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-negate", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_addNoise_item = class + Menuaction "_add Noise" "add noise" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + attenuate = Scale "attenuate" 0 1.0 1.0; + noise = Magick.noise_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-attenuate", + print attenuate.value, + noise._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_normalize_item = class + Menuaction "_Normalize" "normalize" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-normalize", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_opaque_item = class + Menuaction "_Opaque" "change this colour to the fill colour" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + fill = Magick.foreground_widget; + changeColour = Magick.changeCol_widget; + + command = Magick.command [ + "\"%s\"", + fill._flag, + channels._flag, + "-fuzz", + "\"" ++ print changeColour.fuzz.value ++ "%%\"", + (if changeColour.nonMatch then "+" else "-") ++ "opaque", + "\"" ++ changeColour.colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_paint_item = class + Menuaction "_Paint" "simulate an oil painting" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "radius" 10; + + command = Magick.command [ + "\"%s\"", + "-paint", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +/*=== FIXME Bug; remove for now. +Polaroid_item = class + Menuaction "_Polaroid" "simulate a polaroid picture" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "angle" (-90) 90 20; + + command = Magick.command [ + "\"%s\"", + "-polaroid", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} +===*/ + +Magick_posterize_item = class + Menuaction "_Posterize" "reduce to (n) levels per channel" { + action x = class + _result { + _vislevel = 3; + + levels = Expression "levels" 3; + + command = Magick.command [ + "\"%s\"", + "-posterize", + print levels.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_raise_item = class + Menuaction "_Raise" "lighten or darken image edges" { + action x = class + _result { + _vislevel = 3; + + thk = Expression "Thickness" 3; + + command = Magick.command [ + "\"%s\"", + "-raise", + print thk.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_resize_item = class + Menuaction "_Resize" "resize to given width and height" { + action x = class + _result { + _vislevel = 3; + + filter = Magick.filter_widget; + type = Magick.ResizeType_widget; + width = Scale "Width" 1 100 10; + height = Scale "Height" 1 100 10; + isPc = Toggle "Width and height are percent" false; + + command = Magick.command [ + "\"%s\"", + filter._flag, + "-" ++ type._flag, + "\"" ++ print width.value ++ "x" ++ print height.value ++ + (if isPc then "%%" else "!") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_roll_item = class + Menuaction "_Roll" "roll an image horizontally or vertically" { + action x = class + _result { + _vislevel = 3; + + rollx = Expression "X" 3; + rolly = Expression "Y" 3; + + command = Magick.command [ + "\"%s\"", + "-roll", + (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ + (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_rotate_item = class + Menuaction "_Rotate" "rotate" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "angle (degrees)" (-360) 360 20; + virtpixback = Magick.VirtualPixelBack_widget; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + "+distort", + "SRT", + print angle.value, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: -segment, but cluster-threshold should be percentage of image area. + +Magick_sepia_item = class + Menuaction "_Sepia tone" "simulate a sepia-toned photo" { + action x = class + _result { + _vislevel = 3; + + threshold = Scale "Threshold (percent)" 0 100 80; + + command = Magick.command [ + "\"%s\"", + "-sepia-tone", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shade_item = class + Menuaction "_Shade" "shade with a distant light source" { + action x = class + _result { + _vislevel = 3; + + azimuth = Scale "Azimuth (degrees)" (-360) 360 0; + elevation = Scale "Elevation (degrees)" 0 90 45; + + command = Magick.command [ + "\"%s\"", + "-shade", + print azimuth.value ++ "x" ++ print elevation.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shadow_item = class + Menuaction "_Shadow" "simulate a shadow" { + action x = class + _result { + _vislevel = 3; + + shadowCol = Magick.generalcol_widget; + opacity = Scale "Opacity (percent)" 0 100 75; + sigma = Scale "Sigma" 0 30 2; + // FIXME: make offsets a single widget? + offsx = Scale "X-offset" (-20) 20 4; + offsy = Scale "Y-offset" (-20) 20 4; + arePc = Toggle "offsets are percentages" false; + + // FIXME: raw operation creates page offset, which vips dislikes. + // So we take this futher, compositing with source. + command = Magick.command [ + "\"%s\"", + "\"(\" +clone", + "-background", "\"" ++ shadowCol._flag ++ "\"", + "-shadow", + concat [ + "\"", + print opacity.value, "x", print sigma.value, + (if offsx.value >= 0 then "+" else ""), print offsx.value, + (if offsy.value >= 0 then "+" else ""), print offsy.value, + (if arePc then "%%" else ""), + "\"" + ], + "\")\" +swap -background None -layers merge", + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shave_item = class + Menuaction "_Shave" "shave pixels from the edges" { + action x = class + _result { + _vislevel = 3; + + width = Scale "Width" 0 50 10; + height = Scale "Height" 0 50 10; + isPc = Toggle "Width and height are percent" false; + + command = Magick.command [ + "\"%s\"", + "-shave", + "\"" ++ print width.value ++ "x" ++ print height.value ++ + (if isPc then "%%" else "") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shear_item = class + Menuaction "_Shear" "shear along the x-axis and/or y-axis" { + action x = class + _result { + _vislevel = 3; + + shearX = Expression "shear X (degrees)" 0; + shearY = Expression "shear Y (degrees)" 0; + background = Magick.background_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + "-shear", + print shearX.expr ++ "x" ++ print shearY.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_sigmoid_item = class + Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + contrast = Scale "contrast" 0 30 3; + midpoint = Scale "mid-point (percent)" 0 100 50; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "sigmoidal-contrast", + "\"" ++ print contrast.value ++ "x" ++ + print midpoint.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_sketch_item = class + Menuaction "_Sketch" "simulate a pencil sketch" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + angle = Scale "angle" (-360) 360 0; + + command = Magick.command [ + "\"%s\"", + "-sketch", + print radius.value ++ "x" ++ print sigma.value ++ + (if angle >= 0 then ("+" ++ print angle.value) else ""), + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_solarize_item = class + Menuaction "_Solarize" "negate all pixels above a threshold level" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-solarize", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: -sparse-color needs abitrary list of {x,y,colour}. + +Magick_splice_item = class + Menuaction "_Splice" "splice a colour into the image" { + action x = class + _result { + _vislevel = 3; + + background = Magick.background_widget; + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + gravity._flag, + "-splice", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_spread_item = class + Menuaction "_Spread" "displace pixels by random amount" { + action x = class + _result { + _vislevel = 3; + + virtpixback = Magick.VirtualPixelBack_widget; + amount = Expression "Amount (pixels)" 5; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + "-spread", + print amount.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_statistic_item = class + Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { + action x = class + _result { + _vislevel = 3; + + width = Expression "Width" 5; + height = Expression "Height" 5; + statisticType = Magick.StatType_widget; + + command = Magick.command [ + "\"%s\"", + "-statistic", + statisticType._flag, + print width.expr ++ "x" ++ print height.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_swirl_item = class + Menuaction "_Swirl" "swirl around the centre" { + action x = class + _result { + _vislevel = 3; + + angle = Magick.angle_widget; + + command = Magick.command [ + "\"%s\"", + "-swirl", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_thresholdMenu_item = class + Menupullright "_Threshold" "make black or white" { + + Magick_threshold_item = class + Menuaction "_Threshold" "apply black/white threshold" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_blackThreshold_item = class + Menuaction "_Black threshold" "where below threshold set to black" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-black-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_whiteThreshold_item = class + Menuaction "_White threshold" "where above threshold set to white" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-white-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_latThreshold_item = class + Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { + action x = class + _result { + _vislevel = 3; + + width = Expression "Width" 10; + height = Expression "Height" 10; + offset = Scale "Offset (percent)" (-100) 100 0; + // note: "-lat" doesn't respond to channels + + command = Magick.command [ + "\"%s\"", + "-lat", + concat ["\"", print width.expr, "x", print height.expr, + (if offset.value >= 0 then "+" else ""), print offset.value, + "%%\"" + ], + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_randThreshold_item = class + Menuaction "_Random Threshold" "between specified limits, apply random threshold" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + low = Scale "Low threshold" 0 100 10; + high = Scale "High threshold" 0 100 90; + isPc = Toggle "Thresholds are percent" true; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-random-threshold", + "\"" ++ print low.value ++ "x" ++ print high.value ++ + (if isPc then "%%" else "") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end ThresholdMenu_item + + +// Note: alternatives include: +// convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif + +Magick_tile_item = class + Menuaction "_Tile" "fill given size with tiled image" { + action x = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + + command = Magick.command [ + size._flag, + "tile:" ++ "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_tint_item = class + Menuaction "_Tint" "apply a tint" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + amount = Scale "amount (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-tint", + // snibgo note: although the amount is a percentage, it doesn't need "%" character. + print amount.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_transparent_item = class + Menuaction "_Transparent" "make this colour transparent" { + action x = class + _result { + _vislevel = 3; + + changeColour = Magick.changeCol_widget; + + command = Magick.command [ + "\"%s\"", + "-fuzz", + "\"" ++ print changeColour.fuzz.value ++ "%%\"", + (if changeColour.nonMatch then "+" else "-") ++ "transparent", + "\"" ++ changeColour.colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_trim_item = class + Menuaction "_Trim" "trims away border" { + action x = class + _result { + _vislevel = 3; + + fuzz = Magick.fuzz_widget; + + command = Magick.command [ + "\"%s\"", + "-fuzz", + "\"" ++ print fuzz.value ++ "%%\"", + "-trim +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_uniqueCols_item = class + Menuaction "_Unique colours" "discard all but one of any pixel color" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-unique-colors", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_vignette_item = class + Menuaction "_Vignette" "soften the edges in vignette style" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + rx = Scale "Rolloff x (percent)" 0 100 10; + ry = Scale "Rolloff y (percent)" 0 100 10; + background = Magick.background_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + "-vignette", + print radius.value ++ "x" ++ print sigma.value ++ + (if rx.value >= 0 then "+" else "") ++ print rx.value ++ + (if ry.value >= 0 then "+" else "") ++ print ry.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_wave_item = class + Menuaction "_Wave" "shear the columns into a sine wave" { + action x = class + _result { + _vislevel = 3; + + amplitude = Scale "Amplitude (pixels)" 0 100 10; + wavelength = Scale "Wavelength (pixels)" 0 100 10; + + command = Magick.command [ + "\"%s\"", + "-wave", + print amplitude.value ++ "x" ++ print wavelength.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + diff --git a/share/nip2/compat/8.5/Makefile.am b/share/nip2/compat/8.5/Makefile.am new file mode 100644 index 00000000..5de2cdd9 --- /dev/null +++ b/share/nip2/compat/8.5/Makefile.am @@ -0,0 +1,27 @@ +startdir = $(pkgdatadir)/compat/8.5 + +start_DATA = \ + Math.def \ + Image.def \ + Magick.def \ + Colour.def \ + Tasks.def \ + Object.def \ + Filter.def \ + Matrix.def \ + Widgets.def \ + Histogram.def \ + Preferences.ws \ + _joe_extra.def \ + _joe_utilities.def \ + _convert.def \ + _generate.def \ + _list.def \ + _predicate.def \ + _stdenv.def \ + _Object.def \ + _magick.def \ + _types.def + +EXTRA_DIST = $(start_DATA) + diff --git a/share/nip2/compat/8.5/Math.def b/share/nip2/compat/8.5/Math.def new file mode 100644 index 00000000..34a40a66 --- /dev/null +++ b/share/nip2/compat/8.5/Math.def @@ -0,0 +1,588 @@ +Math_arithmetic_item = class + Menupullright "_Arithmetic" "basic arithmetic for objects" { + Add_item = class + Menuaction "_Add" "add a and b" { + action a b = map_binary add a b; + } + + Subtract_item = class + Menuaction "_Subtract" "subtract b from a" { + action a b = map_binary subtract a b; + } + + Multiply_item = class + Menuaction "_Multiply" "multiply a by b" { + action a b = map_binary multiply a b; + } + + Divide_item = class + Menuaction "_Divide" "divide a by b" { + action a b = map_binary divide a b; + } + + Remainder_item = class + Menuaction "_Remainder" + "remainder after integer division of a by b" { + action a b = map_binary remainder a b; + } + + sep1 = Menuseparator; + + Absolute_value_item = class + Menuaction "A_bsolute Value" "absolute value of x" { + action x = map_unary abs x; + } + + Absolute_value_vector_item = class + Menuaction "Absolute Value _Vector" + "like Absolute Value, but treat pixels as vectors" { + action x = map_unary abs_vec x; + } + + Sign_item = class + Menuaction "S_ign" "unit vector" { + action x = map_unary sign x; + } + + Negate_item = class + Menuaction "_Negate" "multiply by -1" { + action x = map_unary unary_minus x; + } +} + +Math_trig_item = class + Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { + Sin_item = class + Menuaction "_Sine" "calculate sine x" { + action x = map_unary sin x; + } + + Cos_item = class + Menuaction "_Cosine" "calculate cosine x" { + action x = map_unary cos x; + } + + Tan_item = class + Menuaction "_Tangent" "calculate tangent x" { + action x = map_unary tan x; + } + + sep1 = Menuseparator; + + Asin_item = class + Menuaction "Arc S_ine" "calculate arc sine x" { + action x = map_unary asin x; + } + + Acos_item = class + Menuaction "Arc C_osine" "calculate arc cosine x" { + action x = map_unary acos x; + } + + Atan_item = class + Menuaction "Arc T_angent" "calculate arc tangent x" { + action x = map_unary atan x; + } + + sep2 = Menuseparator; + + Rad_item = class + Menuaction "_Degrees to Radians" "convert degrees to radians" { + action x = map_unary rad x; + } + + Deg_item = class + Menuaction "_Radians to Degrees" "convert radians to degrees" { + action x = map_unary deg x; + } + + sep3 = Menuseparator; + + Angle_range_item = class + Menuaction "Angle i_n Range" + "is angle within t degrees of r, mod 360" { + action t r angle + = clock (max - angle) < 2*r + { + max = clock (t + r); + + clock a + = a + 360, a < 0; + = a - 360, a >= 360; + = a; + } + } +} + +Math_log_item = class + Menupullright "_Log" "logarithms and anti-logs" { + Exponential_item = class + Menuaction "_Exponential" "calculate e ** x" { + action x = map_unary (power e) x; + } + + Log_natural_item = class + Menuaction "Natural _Log" "log base e of x" { + action x = map_unary log x; + } + + sep1 = Menuseparator; + + Exponential10_item = class + Menuaction "E_xponential base 10" "calculate 10 ** x" { + action x = map_unary (power 10) x; + } + + Log10_item = class + Menuaction "L_og Base 10" "log base 10 of x" { + action x = map_unary log10 x; + } + + sep2 = Menuseparator; + + Raise_to_power_item = class + Menuaction "_Raise to Power" "calculate x ** y" { + action x y = map_binary power x y; + } +} + +Math_complex_item = class + Menupullright "_Complex" "operations on complex numbers and images" { + Complex_extract = class + Menupullright "_Extract" "extract fields from complex" { + Real_item = class + Menuaction "_Real" + "extract real part of complex" { + action in = map_unary re in; + } + + Imaginary_item = class + Menuaction "_Imaginary" + "extract imaginary part of complex" { + action in = map_unary im in; + } + } + + Complex_build_item = class + Menuaction "_Build" "join a and b to make a complex" { + action a b = map_binary comma a b; + } + + sep1 = Menuseparator; + + Polar_item = class + Menuaction "_Polar" + "convert real and imag to amplitude and phase" { + action a = map_unary polar a; + } + + Rectangular_item = class + Menuaction "_Rectagular" + ("convert (amplitude, phase) image to rectangular " ++ + "coordinates") { + action x = map_unary rectangular x; + } + + sep2 = Menuseparator; + + Conjugate_item = class + Menuaction "_Conjugate" "invert imaginary part" { + action x = map_unary conj x; + } +} + +Math_boolean_item = class + Menupullright "_Boolean" "bitwise boolean operations for integer objects" { + And_item = class + Menuaction "_AND" "bitwise AND of a and b" { + action a b = map_binary bitwise_and a b; + } + + Or_item = class + Menuaction "_OR" "bitwise OR of a and b" { + action a b = map_binary bitwise_or a b; + } + + Eor_item = class + Menuaction "_XOR" "bitwise exclusive or of a and b" { + action a b = map_binary eor a b; + } + + Not_item = class + Menuaction "_NOT" "invert a" { + action a = map_unary not a; + } + + sep1 = Menuseparator; + + Right_shift_item = class + Menuaction "Shift _Right" "shift a right by b bits" { + action a b = map_binary right_shift a b; + } + + Left_shift_item = class + Menuaction "Shift _Left" "shift a left by b bits" { + action a b = map_binary left_shift a b; + } + + sep2 = Menuseparator; + + If_then_else_item = class + Menuaction "_If Then Else" + "b where a is non-zero, c elsewhere" { + action a b c + = map_trinary ite a b c + { + // can't use if_then_else, we need a true trinary + ite a b c = if a then b else c; + } + } + + Bandand_item = Image_band_item.Bandand_item; + + Bandor_item = Image_band_item.Bandor_item; +} + +Math_relational_item = class + Menupullright "R_elational" "comparison operations" { + Equal_item = class + Menuaction "_Equal to" "test a equal to b" { + action a b = map_binary equal a b; + } + + Not_equal_item = class + Menuaction "_Not Equal to" "test a not equal to b" { + action a b = map_binary not_equal a b; + } + + sep1 = Menuseparator; + + More_item = class + Menuaction "_More Than" "test a strictly greater than b" { + action a b = map_binary more a b; + } + + Less_item = class + Menuaction "_Less Than" "test a strictly less than b" { + action a b = map_binary less a b; + } + + sep2 = Menuseparator; + + More_equal_item = class + Menuaction "M_ore Than or Equal to" + "test a greater than or equal to b" { + action a b = map_binary more_equal a b; + } + + Less_equal_item = class + Menuaction "L_ess Than or Equal to" + "test a less than or equal to b" { + action a b = map_binary less_equal a b; + } +} + +Math_list_item = class + Menupullright "L_ist" "operations on lists" { + Head_item = class + Menuaction "_Head" "first element in list" { + action x = map_unary hd x; + } + + Tail_item = class + Menuaction "_Tail" "list without the first element" { + action x = map_unary tl x; + } + + Last_item = class + Menuaction "_Last" "last element in list" { + action x = map_unary last x; + } + + Init_item = class + Menuaction "_Init" "list without the last element" { + action x = map_unary init x; + } + + sep1 = Menuseparator; + + Reverse_item = class + Menuaction "_Reverse" "reverse order of elements in list" { + action x = map_unary reverse x; + } + + Sort_item = class + Menuaction "_Sort" "sort list into ascending order" { + action x = map_unary sort x; + } + + Make_set_item = class + Menuaction "_Make Set" "remove duplicates from list" { + action x = map_unary mkset equal x; + } + + Transpose_list_item = class + Menuaction "Tr_anspose" + "exchange rows and columns in a list of lists" { + action x = map_unary transpose x; + } + + Concat_item = class + Menuaction "_Concat" + "flatten a list of lists into a single list" { + action l = map_unary concat l; + } + + sep2 = Menuseparator; + + Length_item = class + Menuaction "L_ength" "find the length of list" { + action x = map_unary len x; + } + + Subscript_item = class + Menuaction "S_ubscript" + "return element n from list (index from zero)" { + action n x = map_binary subscript n x; + } + + Take_item = class + Menuaction "_Take" "take the first n elements of list x" { + action n x = map_binary take n x; + } + + Drop_item = class + Menuaction "_Drop" "drop the first n elements of list x" { + action n x = map_binary drop n x; + } + + sep3 = Menuseparator; + + Join_item = class + Menuaction "_Join" "join two lists end to end" { + action a b = map_binary join a b; + } + + Difference_item = class + Menuaction "_Difference" "difference of two lists" { + action a b = map_binary difference a b; + } + + Cons_item = class + Menuaction "C_ons" "put element a on the front of list x" { + action a x = map_binary cons a x; + } + + Zip_item = class + Menuaction "_Zip" "join two lists, pairwise" { + action a b = map_binary zip2 a b; + } +} + +Math_round_item = class + Menupullright "_Round" "various rounding operations" { + /* smallest integral value not less than x + */ + Ceil_item = class + Menuaction "_Ceil" "smallest integral value not less than x" { + action x = map_unary ceil x; + } + + Floor_item = class + Menuaction "_Floor" + "largest integral value not greater than x" { + action x = map_unary floor x; + } + + Rint_item = class + Menuaction "_Round to Nearest" "round to nearest integer" { + action x = map_unary rint x; + } +} + +Math_fourier_item = class + Menupullright "_Fourier" "Fourier transform" { + Forward_item = class + Menuaction "_Forward" "fourier transform of image" { + action a = map_unary (rotquad @ fwfft) a; + } + + Reverse_item = class + Menuaction "_Reverse" "inverse fourier transform of image" { + action a = map_unary (invfft @ rotquad) a; + } + + Rotate_quadrants_item = class + Menuaction "Rotate _Quadrants" "rotate quadrants" { + action a = map_unary rotquad a; + } +} + +Math_stats_item = class + Menupullright "_Statistics" "measure various statistics of objects" { + Value_item = class + Menuaction "_Value" "value of point in object" { + action a = class _result { + _vislevel = 3; + + position = Expression "Coordinate" (0, 0); + + _result = map_binary point position.expr a; + } + } + + Mean_item = class + Menuaction "_Mean" "arithmetic mean value" { + action a = map_unary mean a; + } + + Gmean_item = class + Menuaction "_Geometric Mean" "geometric mean value" { + action a = map_unary meang a; + } + + Zmean_item = class + Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { + action a = map_unary meanze a; + } + + Deviation_item = class + Menuaction "_Standard Deviation" "standard deviation of object" { + action a = map_unary deviation a; + } + + Zdeviation_item = class + Menuaction "Z_ero-excluding Standard Deviation" + "standard deviation of non-zero elements" { + action a = map_unary deviationze a; + } + + Skew_item = class + Menuaction "S_kew" "skew of image or list or vector" { + action a = map_unary skew a; + } + + Kurtosis_item = class + Menuaction "Kurtosis" "kurtosis of image or list or vector" { + action a = map_unary kurtosis a; + } + + Stats_item = class + Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { + action a = map_unary stats a; + } + + sep1 = Menuseparator; + + Max_item = class + Menuaction "M_aximum" "maximum of object" { + action a = map_unary max a; + } + + Min_item = class + Menuaction "M_inimum" "minimum of object" { + action a = map_unary min a; + } + + Maxpos_item = class + Menuaction "_Position of Maximum" "position of maximum in object" { + action a = map_unary maxpos a; + } + + Minpos_item = class + Menuaction "P_osition of Minimum" "position of minimum in object" { + action a = map_unary minpos a; + } + + Gravity_item = class + Menuaction "Centre of _Gravity" + "position of centre of gravity of histogram" { + action a = map_unary gravity a; + } + + sep2 = Menuseparator; + + Count_set_item = class + Menuaction "_Non-zeros" "number of non-zero elements in object" { + action a + = map_unary cset a + { + cset i = (mean (i != 0) * i.width * i.height) / 255; + } + } + + Count_clear_item = class + Menuaction "_Zeros" "number of zero elements in object" { + action a + = map_unary cclear a + { + cclear i = (mean (i == 0) * i.width * i.height) / 255; + } + } + + Count_edges_item = class + Menuaction "_Edges" + "count average edges across or down image" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Count" [ + "Horizontal lines", + "Vertical lines" + ] 0; + + _result + = map_unary process x + { + process image = Number (edge.labels?edge) + (im_cntlines image.value edge.value); + } + } + } + + sep3 = Menuseparator; + + Linear_regression_item = class + Menuaction "_Linear Regression" "fit a line to a set of points" { + action xes yes = linreg xes yes; + } + + Weighted_linear_regression_item = class + Menuaction "_Weighted Linear Regression" + "fit a line to a set of points and deviations" { + action xes yes devs = linregw xes yes devs; + } + + Cluster_item = class + Menuaction "_Cluster" "cluster a list of numbers" { + action l = class { + _vislevel = 3; + + thresh = Expression "Threshold" 10; + + [_r, _w] = cluster thresh.expr l; + + result = _r; + weights = _w; + } + } +} + +Math_base_item = class + Menupullright "Bas_e" "convert number bases" { + Hexadecimal_item = class + Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { + action a = map_unary (print_base 16) a; + } + + Binary_item = class + Menuaction "_Binary" "convert to binary (base 2)" { + action a = map_unary (print_base 2) a; + } + + Octal_item = class + Menuaction "_Octal" "convert to octal (base 8)" { + action a = map_unary (print_base 8) a; + } +} diff --git a/share/nip2/compat/8.5/Matrix.def b/share/nip2/compat/8.5/Matrix.def new file mode 100644 index 00000000..944e8d77 --- /dev/null +++ b/share/nip2/compat/8.5/Matrix.def @@ -0,0 +1,537 @@ + +Matrix_build_item = class + Menupullright "_New" "make a new matrix of some sort" { + + Plain_item = class + Menuaction "_Plain" "make a new plain matrix widget" { + action = Matrix (identity_matrix 3); + } + + Convolution_item = class + Menuaction "_Convolution" "make a new convolution matrix widget" { + action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; + } + + Recombination_item = class + Menuaction "_Recombination" + "make a new recombination matrix widget" { + action = Matrix_rec (identity_matrix 3); + } + + Morphology_item = class + Menuaction "_Morphology" "make a new morphology matrix widget" { + action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; + } + + sep1 = Menuseparator; + + Matrix_identity_item = class + Menuaction "_Identity" "make an identity matrix" { + action = identity (identity_matrix 5); + + identity v = class + _result { + _vislevel = 3; + + s = Expression "Size" (len v); + + _result + = Matrix (identity_matrix (to_real s)), to_real s != len v; + = Matrix v; + + Matrix_vips value scale offset filename display = identity value; + } + } + + Matrix_series_item = class + Menuaction "_Series" "make a series" { + action = series (mkseries 0 1 5); + + mkseries s t e + = transpose [[to_real s, to_real s + to_real t .. to_real e]]; + + series v = class + _result { + _vislevel = 3; + + _s = v?0?0; + _t = v?1?0 - v?0?0; + _e = (last v)?0; + + s = Expression "Start value" _s; + t = Expression "Step by" _t; + e = Expression "End value" _e; + + _result + = Matrix (mkseries s t e), + to_real s != _s || to_real t != _t || to_real e != _e + = Matrix v; + + Matrix_vips value scale offset filename display = series value; + } + } + + Matrix_square_item = class + Menuaction "_Square" "make a square matrix" { + action = square (mksquare 5); + + mksquare s = replicate s (take s [1, 1 ..]); + + square v = class + _result { + _vislevel = 3; + + s = Expression "Size" (len v); + + _result + = Matrix_con (sum v) 0 v, len v == to_real s + = Matrix_con (sum m) 0 m + { + m = mksquare (to_real s); + } + + Matrix_vips value scale offset filename display = square value; + } + } + + Matrix_circular_item = class + Menuaction "_Circular" "make a circular matrix" { + action = circle (mkcircle 3); + + mkcircle r + = map2 (map2 pyth) xes yes + { + line = [-r .. r]; + xes = replicate (2 * r + 1) line; + yes = transpose xes; + pyth a b + = 1, (a**2 + b**2) ** 0.5 <= r + = 0; + } + + circle v = class + _result { + _vislevel = 3; + + r = Expression "Radius" ((len v - 1) / 2); + + _result + = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 + = Matrix_con (sum m) 0 m + { + m = mkcircle (to_real r); + } + + Matrix_vips value scale offset filename display = circle value; + } + } + + Matrix_gaussian_item = class + Menuaction "_Gaussian" "make a gaussian matrix" { + action = class + _result { + _vislevel = 3; + + s = Scale "Sigma" 0.001 10 1; + ma = Scale "Minimum amplitude" 0 1 0.2; + integer = Toggle "Integer" false; + + _result + = fn s.value ma.value + { + fn + = im_gauss_imask, integer + = im_gauss_dmask; + } + } + } + + Matrix_laplacian_item = class + Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { + action = class + _result { + _vislevel = 3; + + s = Scale "Sigma" 0.001 10 1.5; + ma = Scale "Minimum amplitude" 0 1 0.1; + integer = Toggle "Integer" false; + + _result + = fn s.value ma.value + { + fn + = im_log_imask, integer + = im_log_dmask; + } + } + } + +} + +Matrix_to_matrix_item = class + Menuaction "Con_vert to Matrix" "convert anything to a matrix" { + action x = to_matrix x; +} + +#separator + +Matrix_extract_item = class + Menupullright "_Extract" "extract rows or columns from a matrix" { + Rows_item = class + Menuaction "_Rows" "extract rows" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from row" 0; + number = Expression "Extract this many rows" 1; + + _result + = map_unary process x + { + process x + = extract_area 0 first (get_width x) number x; + } + } + } + + Columns_item = class + Menuaction "_Columns" "extract columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from column" 0; + number = Expression "Extract this many columns" 1; + + _result + = map_unary process x + { + process mat + = extract_area first 0 number (get_height x) x; + } + } + } + + Area_item = class + Menuaction "_Area" "extract area" { + action x = class + _result { + _vislevel = 3; + + left = Expression "First column" 0; + top = Expression "First row" 0; + width = Expression "Number of columns" 1; + height = Expression "Number of rows" 1; + + _result + = map_unary process x + { + process mat + = extract_area left top width height x; + } + } + } + + Diagonal_item = class + Menuaction "_Diagonal" "extract diagonal" { + action x = class + _result { + _vislevel = 3; + + which = Option "Extract" [ + "Leading Diagonal", + "Trailing Diagonal" + ] 0; + + _result + = map_unary process x + { + process mat + = mat.Matrix_base (map2 extr [0..] mat.value), + which == 0 + = mat.Matrix_base (map2 extr + [mat.width - 1, mat.width - 2 .. 0] mat.value); + extr n l = [l?n]; + } + } + } +} + +Matrix_insert_item = class + Menupullright "_Insert" "insert rows or columns into a matrix" { + // make a new 8-bit uchar image of wxh with pixels set to p + // use to generate new cells + newim w h p + = image_new w h 1 + Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; + + Rows_item = class + Menuaction "_Rows" "insert rows" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Insert at row" 0; + number = Expression "Insert this many rows" 1; + item = Expression "Set new cells to" 0; + + _result + = map_unary process x + { + process x + = foldl1 join_tb (concat [top, new, bottom]) + { + top + = [extract_area 0 0 w f x], f > 0 + = []; + new = [(if is_Matrix x then to_matrix else id) + (newim w number item.expr)]; + bottom + = [extract_area 0 f w (h - f) x], f < h + = []; + + f = to_real first; + w = get_width x; + h = get_height x; + } + } + } + } + + Columns_item = class + Menuaction "_Columns" "insert columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Insert at column" 0; + number = Expression "Insert this many columns" 1; + item = Expression "Set new cells to" 0; + + _result + = map_unary process x + { + process x + = foldl1 join_lr (concat [left, new, right]) + { + left + = [extract_area 0 0 f h x], f > 0 + = []; + new = [(if is_Matrix x then to_matrix else id) + (newim number h item.expr)]; + right + = [extract_area f 0 (w - f) h x], f < w + = []; + + f = to_real first; + w = get_width x; + h = get_height x; + } + } + } + } +} + +Matrix_delete_item = class + Menupullright "_Delete" "delete rows or columns from a matrix" { + // remove number of items, starting at first + delete first number l = take (to_real first) l ++ + drop (to_real first + to_real number) l; + + Rows_item = class + Menuaction "_Rows" "delete rows" { + + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from row" 0; + number = Expression "Delete this many rows" 1; + + _result + = map_unary process x + { + process x + = foldl1 join_tb (concat [top, bottom]) + { + top + = [extract_area 0 0 w f x], f > 0 + = []; + bottom + = [extract_area 0 b w (h - b) x], b < h + = []; + + f = to_real first; + n = to_real number; + b = f + n; + w = get_width x; + h = get_height x; + } + } + } + } + + Columns_item = class + Menuaction "_Columns" "delete columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from column" 0; + number = Expression "Delete this many columns" 1; + + _result + = map_unary process x + { + process x + = foldl1 join_lr (concat [left, right]) + { + left + = [extract_area 0 0 f h x], f > 0 + = []; + right + = [extract_area r 0 (w - r) h x], r < w + = []; + + f = to_real first; + n = to_real number; + r = f + n; + w = get_width x; + h = get_height x; + } + } + } + } +} + +Matrix_join = class + Menupullright "_Join" "join two matricies" { + Left_right_item = class + Menuaction "_Left to Right" "join two matricies left-right" { + action a b = map_binary join_lr a b; + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { + action a b = map_binary join_tb a b; + } +} + +Matrix_rotate_item = class + Menupullright "_Rotate" "clockwise rotation by fixed angles" { + + rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; + + rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; + + rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; + + Matrix_rot45_item = class + Menuaction "_45 Degrees" + "45 degree rotate (square, odd-length-sides only)" { + action x = map_unary rot45 x; + } +} + +Matrix_flip_item = Image_transform_item.Flip_item; + +Matrix_sort_item = class + Menuaction "_Sort" "sort by column or row" { + action x = class + _result { + _vislevel = 3; + + o = Option (_ "Orientation") [ + _ "Sort by column", + _ "Sort by row" + ] 0; + i = Expression (_ "Sort on index") 0; + d = Option (_ "Direction") [ + _ "Ascending", + _ "Descending" + ] 0; + + _result + = map_unary sort x + { + idx = to_real i; + pred a b + = a?idx <= b?idx, d == 0 + = a?idx >= b?idx; + sort x + = (x.Matrix_base @ sortc pred) x.value, + o == 0 + = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; + } + } +} + +#separator + +Matrix_invert_item = class + Menuaction "In_vert" "calculate inverse matrix" { + action x = map_unary (converse power (-1)) x; +} + +Matrix_transpose_item = class + Menuaction "_Transpose" "swap rows and columns" { + action x = map_unary transpose x; +} + +#separator + +Matrix_plot_scatter_item = class + Menuaction "_Plot Scatter" + "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { + action x = class + _result { + _check_args = [ + [x, "x", check_Matrix] + ]; + _vislevel = 3; + + auto = Toggle "Auto Range" true; + xmin = Expression "X range minimum" 0; + xmax = Expression "X range maximum" 1; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + + _result + = Plot options ((x2b @ get_image @ to_image) x) + { + options + = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ + range; + range + = [], auto + = [$xmin => xmin.expr, $xmax => xmax.expr, + $ymin => ymin.expr, $ymax => ymax.expr]; + + // matrix to image makes a 1-band mxn image + // we need to put columns into bands + x2b im + = bandjoin (map extract_col [0 .. w - 1]) + { + w = get_width im; + h = get_height im; + b = get_bands im; + extract_col x = extract_area x 0 1 h im; + } + } + } +} + +Matrix_plot_item = Hist_plot_item; + +Matrix_buildlut_item = class + Menuaction "_Build LUT From Scatter" + "make a lookup table from a matrix of [x,y1,y2..] coordinates" { + action x = class + _result { + _check_args = [ + [x, "x", check_Matrix] + ]; + _result = buildlut x; + } +} diff --git a/share/nip2/compat/8.5/Object.def b/share/nip2/compat/8.5/Object.def new file mode 100644 index 00000000..b1585186 --- /dev/null +++ b/share/nip2/compat/8.5/Object.def @@ -0,0 +1,49 @@ +Object_duplicate_item = class + Menuaction "_Duplicate" "take a copy of an object" { + action x = map_unary copy x; +} + +#separator + +Object_list_to_group_item = class + Menuaction "_List to Group" "turn a list of objects into a group" { + action x = to_group x; +} + +Object_group_to_list_item = class + Menuaction "_Group to List" "turn a group into a list of objects" { + action x = to_list x; +} + +#separator + +Object_break_item = class + Menuaction "_Break Up Object" + "break an object into a list of components" { + action x + = map_unary break x + { + break x + = bandsplit x, is_Image x + = map Vector x.value, is_Matrix x + = x.value, is_Vector x || is_Real x + = error "Breakup: not Image/Matrix/Vector/Real"; + } +} + +Object_assemble_item = class + Menuaction "_Assemble Objects" + "assemble a list of objects into a single object" { + action x + = map_unary ass x + { + ass x + = [], x == [] + = Vector x, is_real_list x + = Matrix x, is_matrix x + = bandjoin x, is_listof is_Image x + = Vector (map get_value x), is_listof is_Real x + = Matrix (map get_value x), is_listof is_Vector x + = error "Assemble: not list of Image/Vector/Real/image/real"; + } +} diff --git a/share/nip2/compat/8.5/Preferences.ws b/share/nip2/compat/8.5/Preferences.ws new file mode 100644 index 00000000..37a24978 --- /dev/null +++ b/share/nip2/compat/8.5/Preferences.ws @@ -0,0 +1,919 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/nip2/compat/8.5/Tasks.def b/share/nip2/compat/8.5/Tasks.def new file mode 100644 index 00000000..d0c6d711 --- /dev/null +++ b/share/nip2/compat/8.5/Tasks.def @@ -0,0 +1,952 @@ +Tasks_capture_item = class + Menupullright "_Capture" + "useful stuff for capturing and preprocessing images" { + + Csv_import_item = class + Menuaction "_CSV Import" "read a file of comma-separated values" { + action = class + _result { + _vislevel = 3; + + path = Pathname "File to load" "empty"; + start_line = Expression "Start at line" 1; + rows = Expression "Lines to read (-1 for whole file)" (-1); + whitespace = String "Whitespace characters" " \""; + separator = String "Separator characters" ",;\t"; + + _result + = Image blank, path.value == "empty" + = Image (im_csv2vips filename) + { + filename = search (expand path.value) ++ ":" ++ + "skip:" ++ print (start_line.expr - 1) ++ "," ++ + "whi:" ++ escape whitespace.value ++ "," ++ + "sep:" ++ escape separator.value ++ "," ++ + "line:" ++ print rows.expr; + + // prefix any ',' with a '\' in the separators line + escape x + = foldr prefix [] x + { + prefix x l + = '\\' : x : l, x == ',' + = x : l; + } + + blank = image_new 1 1 1 + Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W + 0 0 0; + } + } + } + + Raw_import_item = class + Menuaction "_Raw Import" "read a file of binary values" { + action = class + _result { + _vislevel = 3; + + path = Pathname "File to load" "empty"; + across = Expression "Pixels across" 100; + down = Expression "Pixels down" 100; + bytes = Expression "Bytes per pixel" 3; + skip = Expression "Skip over initial bytes" 0; + + _result + = Image blank, path.value == "empty" + = Image (im_binfile path.value + across.expr down.expr bytes.expr skip.expr) + { + blank = image_new 1 1 1 + Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W + 0 0 0; + } + } + } + + // interpret Analyze header for layout and calibration + Analyze7_header_item = class + Menuaction "_Interpret Analyze 7 Header" + "examine the Analyze header and set layout and value" { + action x + = x''' + { + // read bits of header + dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); + dim0 = dim 0 x; + dim1 = dim 1 x; + dim2 = dim 2 x; + dim3 = dim 3 x; + dim4 = dim 4 x; + dim5 = dim 5 x; + dim6 = dim 6 x; + dim7 = dim 7 x; + glmax = get_header "dsr-image_dimension.glmax" x; + cal_max = get_header "dsr-image_dimension.cal_max" x; + + // oops, now a nop + x' = x; + + // lay out higher dimensions width-ways + x'' + = grid dim2 dim3 1 x', dim0 == 3 + = grid dim2 dim3 dim4 x', dim0 == 4 + = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 + = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 + = grid (dim2 * dim4 * dim6) dim7 1 + (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', + dim0 == 7 + = error (_ "unsupported dimension " ++ dim0); + + // multiply by scale factor to get kBeq + x''' = x'' * (cal_max / glmax); + } + } + + Video_item = class + Menuaction "Capture _Video Frame" "capture a frame of still video" { + // shortcut to prefs + prefs = Workspaces.Preferences; + + action = class + _result { + _vislevel = 3; + + device = prefs.VIDEO_DEVICE; + channel = Option "Input channel" [ + "TV", + "Composite 1", + "Composite 2", + "Composite 3" + ] prefs.VIDEO_CHANNEL; + b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; + col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; + con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; + hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; + frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; + mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; + crop = Toggle "Crop image" prefs.VIDEO_CROP; + + // grab, but hide it ... if we let the crop edit + _raw_grab = Image (im_video_v4l1 device channel.value + b.value col.value con.value + hue.value frames.value); + + edit_crop + = Region _raw_grab left top width height + { + left = prefs.VIDEO_CROP_LEFT; + top = prefs.VIDEO_CROP_TOP; + width = min_pair + prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); + height = min_pair + prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); + } + + aspect_ratio = Expression "Stretch vertically by" + prefs.VIDEO_ASPECT; + + _result + = frame' + { + frame + = edit_crop, crop + = _raw_grab; + + frame' + = colour_transform_to Image_type.B_W frame, mono + = frame; + } + } + } + + Smooth_image_item = class + Menuaction "_Smooth" "remove small features from image" { + action in = class + _result { + _vislevel = 3; + + feature = Scale "Minimum feature size" 1 50 20; + + _result = map_unary (smooth feature.value) in; + } + } + + Light_correct_item = class + Menuaction "_Flatfield" "use white image w to flatfield image i" { + action w i + = map_binary wc w i + { + wc w i + = clip2fmt i.format (w' * i) + { + fac = mean w / max w; + w' = fac * (max w / w); + } + } + } + + Image_rank_item = Filter_rank_item.Image_rank_item; + + Tilt_item = Filter_tilt_item; + + sep1 = Menuseparator; + + White_balance_item = class + Menuaction "_White Balance" + "use average of small image to set white of large image" { + action a b = class + _result { + _vislevel = 3; + + white_hint = "Set image white to:"; + white = Colour_picker "Lab" [100, 0, 0]; + + _result + = map_binary wb a b + { + wb a b + = colour_transform_to (get_type image) image_xyz' + { + area x = x.width * x.height; + larger x y = area x > area y; + [image, patch] = sortc larger [a, b]; + to_xyz = colour_transform_to Image_type.XYZ; + + // white balance in XYZ + patch_xyz = to_colour (to_xyz patch); + white_xyz = to_xyz white; + + facs = (mean patch_xyz / mean white_xyz) * + (white_xyz / patch_xyz); + + image_xyz = to_xyz image; + image_xyz' = image_xyz * facs; + } + } + } + } + + Gamma_item = Image_levels_item.Gamma_item; + + Tone_item = Image_levels_item.Tone_item; + + sep2 = Menuseparator; + + Crop_item = Image_crop_item; + + Rotate_item = Image_transform_item.Rotate_item; + + Flip_item = Image_transform_item.Flip_item; + + Resize_item = Image_transform_item.Resize_item; + + Rubber_item = Image_transform_item.Image_rubber_item; + + sep3 = Menuseparator; + + ICC_item = Colour_icc_item; + + Temp_item = Colour_temperature_item; + + Find_calib_item = class + Menuaction "Find _Colour Calibration" + "find an RGB -> XYZ transform from an image of a colour chart" { + action image = class + _result { + _check_args = [ + [image, "image", check_Image] + ]; + _vislevel = 3; + + measure = Scale (_ "Measure area (%)") 1 100 50; + + sample = measure_draw 6 4 (to_real measure) image; + + // get macbeth data file to use + macbeth = Pathname "Pick a Macbeth data file" + "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; + + mode = Option "Input LUT" [ + "Linearize from chart greyscale", + "Fit intercept from chart greyscale", + "Linear input, set brightness from chart", + "Linear input" + ] 0; + + // get max of input image + _max_value = Image_format.maxval image.format; + + // measure chart image + _camera = measure_sample 6 4 (to_real measure) image; + + // load true values + _true_Lab = Matrix_file macbeth.value; + _true_XYZ = colour_transform + Image_type.LAB Image_type.XYZ _true_Lab; + + // get Ys of greyscale + _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); + + // camera greyscale (all bands) + _camera_grey = drop 18 _camera.value; + + // normalise both to 0-1 and combine + _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; + _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; + _comb + = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 + = Matrix [0: intercepts, replicate (_camera.width + 1) 1], + mode == 1 + = Matrix [[0, 0], [1, 1]] + { + intercepts = [(linreg _true_grey_Y' cam).intercept :: + cam <- transpose _camera_grey']; + } + + // make a linearising lut ... zero on left + _linear_lut = im_invertlut _comb (_max_value + 1); + + // and display it + // plot from 0 explicitly so we see the effect of mode1 (intercept + // from greyscale) + linearising_lut = Plot [$ymin => 0] _linear_lut; + + // map an image though the lineariser + linear x + = hist_map linearising_lut.value x, mode == 0 || mode == 1 + = x; + + // map the chart measurements though the lineariser + _camera' = (to_matrix @ linear @ to_image) _camera; + + // solve for RGB -> XYZ + // normalise: the 2nd row is what makes Y, so divide by that to + // get Y in 0-1. + _pinv = (transpose _camera' * _camera') ** -1; + _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); + M = _full_M / scale; + scale = sum _full_M.value?1; + + // now turn the camera to LAB and calculate dE76 + _camera'' = (to_matrix @ + colour_transform Image_type.XYZ Image_type.LAB @ + recomb M @ + multiply scale @ + to_image) _camera'; + + _dEs = map abs_vec (_camera'' - _true_Lab).value; + avg_dE76 = mean _dEs; + _max_dE = foldr max_pair 0 _dEs; + _worst = index (equal _max_dE) _dEs; + worst_patch + = name _worst ++ " (patch " ++ + print (_worst + 1) ++ ", " ++ + print _max_dE ++ " dE)" + { + name i + = macbeth_names?i, i >= 0 && i < len macbeth_names + = "Unknown"; + } + + // normalise brightness ... in linear mode, we optionally don't + // set the brightness from the Macbeth chart + norm x + = x * scale, mode != 3 + = x; + + // convert RGB camera to Lab + _result = (Image @ + colour_transform Image_type.XYZ Image_type.LAB @ + norm @ + recomb M @ + cast_float @ + linear) image.value; + } + } + + Apply_calib_item = class + Menuaction "_Apply Colour Calibration" + "apply an RGB -> LAB transform to an image" { + action a b = class + (map_binary process a b) { + process a b + = result, is_instanceof calib_name calib && is_Image image + = error (_ "bad arguments to " ++ "Calibrate_image") + { + // the name of the calib object we need + calib_name = "Tasks_capture_item.Find_calib_item.action"; + + // get the Calibrate_chart arg first + [image, calib] = sortc (const (is_instanceof calib_name)) + [a, b]; + + result = (Image @ + colour_transform Image_type.XYZ Image_type.LAB @ + calib.norm @ + recomb calib.M @ + cast_float @ + calib.linear) image.value; + } + } + } + + sep4 = Menuseparator; + + Graph_hist_item = Hist_find_item; + + Graph_bands_item = class + Menuaction "Plot _Bands" "show image bands as a graph" { + action x = class + _result { + _vislevel = 3; + + style = Option_enum "Style" Plot_style.names "Line"; + + auto = Toggle "Auto Range" true; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + + _result + = Plot options (to_image (bands (image x))).value + { + options + = [$style => style.value] ++ + if auto then [] else + [$ymin => ymin.expr, $ymax => ymax.expr]; + + // try to make something image-like from it + image x + = extract_area x.left x.top 1 1 x.image, is_Mark x + = get_image x, has_image x + = get_image (to_image x); + + // get as [[1],[2],[3]] + bands x + = transpose [map mean (bandsplit x)]; + } + } + } +} + +Tasks_mosaic_item = class + Menupullright "_Mosaic" "build image mosaics" { + /* Check and group a point list by image. + */ + mosaic_sort_test l + = error "mosaic: not all points", + !is_listof is_Mark l + = error "mosaic: points not on two images", + !is_list_len 2 images + = error "mosaic: images do not match in format and coding", + !all_equal (map get_format l) || !all_equal (map get_coding l) + = error "mosaic: not same number of points on each image", + !foldr1 equal (map len l') + = l' + { + // test for all elements of a list equal + all_equal l = all (map (equal (hd l)) (tl l)); + + // all the different images + images = mkset pointer_equal (map get_image l); + + // find all points defined on image + test_image image p = (get_image p) === image; + find l image = filter (test_image image) l; + + // group point list by image + l' = map (find l) images; + } + + /* Sort a point group to get right before left, and within each group to + * get above before below. + */ + mosaic_sort_lr l + = l'' + { + // sort to get upper point first + above a b = a.top < b.top; + l' = map (sortc above) l; + + // sort to get right group before left group + right a b = a?0.left > b?0.left; + l'' = sortc right l'; + } + + /* Sort a point group to get top before bottom, and within each group to + * get left before right. + */ + mosaic_sort_tb l + = l'' + { + // sort to get upper point first + left a b = a.left < b.left; + l' = map (sortc left) l; + + // sort to get right group before left group + below a b = a?0.top > b?0.top; + l'' = sortc below l'; + } + + /* Put 'em together! Group by image, sort vertically (or horizontally) with + * one of the above, transpose to get pairs matched up, and flatten again. + */ + mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; + + Mosaic_1point_item = class + Menupullright "_One Point" "join two images with a single tie point" { + check_ab_args a b = [ + [a, "a", check_Mark], + [b, "b", check_Mark] + ]; + + // shortcut to prefs + prefs = Workspaces.Preferences; + search_area = prefs.MOSAIC_WINDOW_SIZE; + object_size = prefs.MOSAIC_OBJECT_SIZE; + blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; + refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; + + lr_mos _refine a b = class + Image _result { + _check_args = check_ab_args a b; + + bw = blend_width_widget; + refine = _refine; + + _result + = im_lrmosaic a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + (object_size / 2) (search_area / 2) 0 bw.value, + refine + = im_lrmerge a'.image.value b'.image.value + (b'.left - a'.left) (b'.top - a'.top) bw.value + { + [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; + } + } + + tb_mos _refine a b = class + Image _result { + _check_args = check_ab_args a b; + + bw = blend_width_widget; + refine = _refine; + + _result + = im_tbmosaic a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + (object_size / 2) (search_area / 2) 0 bw.value, + refine + = im_tbmerge a'.image.value b'.image.value + (b'.left - a'.left) (b'.top - a'.top) bw.value + { + [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; + } + } + + Left_right_item = class + Menuaction "_Left to Right" + "join two images left-right with a single tie point" { + action a b = lr_mos refine_widget a b; + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" + "join two images top-bottom with a single tie point" { + action a b = tb_mos refine_widget a b; + } + + sep1 = Menuseparator; + + Left_right_manual_item = class + Menuaction "Manual L_eft to Right" + "join left-right, no auto-adjust of tie points" { + action a b = lr_mos false a b; + } + + Top_bottom_manual_item = class + Menuaction "Manual T_op to Bottom" + "join top-bottom, no auto-adjust of tie points" { + action a b = tb_mos false a b; + } + } + + Mosaic_2point_item = class + Menupullright "_Two Point" "join two images with two tie points" { + check_abcd_args a b c d = [ + [a, "a", check_Mark], + [b, "b", check_Mark], + [c, "c", check_Mark], + [d, "d", check_Mark] + ]; + + // shortcut to prefs + prefs = Workspaces.Preferences; + search_area = prefs.MOSAIC_WINDOW_SIZE; + object_size = prefs.MOSAIC_OBJECT_SIZE; + blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; + refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; + + Left_right_item = class + Menuaction "_Left to Right" + "join two images left-right with a pair of tie points" { + action a b c d = class + Image _result { + _check_args = check_abcd_args a b c d; + + bw = blend_width_widget; + refine = refine_widget; + + _result + = im_lrmosaic1 a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + (object_size / 2) (search_area / 2) + 0 bw.value, + refine + = im_lrmerge1 a'.image.value b'.image.value + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + bw.value + { + [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; + } + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" + "join two images top-bottom with a pair of tie points" { + action a b c d = class + Image _result { + _check_args = check_abcd_args a b c d; + + bw = blend_width_widget; + refine = refine_widget; + + _result + = im_tbmosaic1 a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + (object_size / 2) (search_area / 2) + 0 bw.value, + refine + = im_tbmerge1 a'.image.value b'.image.value + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + bw.value + { + [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; + } + } + } + } + + sep1 = Menuseparator; + + Balance_item = class + Menuaction "Mosaic _Balance" + "disassemble mosaic, scale brightness to match, reassemble" { + action x + = map_unary balance x + { + balance x + = oo_unary_function balance_op x, is_class x + = im_global_balancef x + Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, + is_image x + = error (_ "bad arguments to " ++ "balance") + { + balance_op = Operator "balance" balance + Operator_type.COMPOUND_REWRAP false; + } + } + } + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Manual_balance_item = class + Menupullright "Manual B_alance" "balance tonality of user defined areas" { + prefs = Workspaces.Preferences; + + //////////////////////////////////////////////////////////////////////////////////// + Balance_find_item = class + Menuaction "_Find Values" + "calculates values required to scale and offset balance user defined areas in a given image" + /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary + * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of + * 8-bit reference masks, where the masks are white on a black background. + */ + { + action im_in m_control m_group = class + Matrix values{ + _vislevel = 1; + + _control_im = if m_control then im_in else 0; + _control_meanmax = so_meanmax _control_im; + _group_check = is_Group m_group; + _m_list = m_group.value, _group_check + = m_group; + + process m_current mat_in = mat_out + {so_values = so_calculate _control_meanmax im_in m_current; + mat_out = join [so_values] mat_in;} + + values = (foldr process [] _m_list); + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Balance_check_item = class + Menuaction "_Check Values" + "allows calculated set of scale and offset values to be checked and adjusted if required" + /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. + * Eg. Check values required to balance the secondary structure in an X-ray image. + * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, + * where the masks are white on a black background. + */ + { + action im_in m_matrix m_group = class + Image value + { + _vislevel = 3; + + blur = Scale "Blur" 1 10 1; + _blur = (blur.value/2 + 0.5), blur.value > 1 + = 1; + + _group_check = is_Group m_group; + _m_list = m_group.value, _group_check + = m_group; + + adjust = Matrix_rec mat_a + { + no_masks = len _m_list; + mat_a = replicate no_masks [0, 0]; + } + + // Apply the user defined adjustments to the inputted matrix of scale and offset values + _adjusted = map2 fn_adjust m_matrix.value adjust.value; + fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; + + _scaled_ims = map (fn_so_apply im_in) _adjusted; + fn_so_apply im so = map_unary adj im + {adj im = im * (so?0) + (so?1);} + _im_pairs = zip2 _m_list _scaled_ims; + + // Prepare black images as starting point. //////////// + _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; + _pair_start = [(_blank + 1), _blank]; + + Build = Toggle "Build Scale and Offset Correction Images" false; + + Output = class + { + _vislevel = 1; + scale_im = _build?0; + offset_im = _build?1; + so_values = Matrix _adjusted; + + _build = [Image so_images?0, Image so_images?1], Build + = ["Scale image not built.", "Offset image not built."] + { + m_list' = transpose [_m_list]; + m_all = map2 join m_list' _adjusted; + so_images = foldr process_2 _pair_start m_all; + } + } + + value = (foldr process_1 im_in_b _im_pairs).value + {im_in_b = map_unary cast_float im_in;} + + process_1 m_current im_start + = im_out + { + bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); + blended_im = im_blend bl_mask (m_current?1).value im_start.value; + im_out = Image (clip2fmt im_start.format blended_im); + } + + // Process for building scale and offset image. + process_2 current p_start + = p_out + { + im_s = if ((current?0) > 128) then current?1 else _blank; + im_o = if ((current?0) > 128) then current?2 else _blank; + + im_s' = convsep (matrix_blur _blur) (im_s != 0); + im_o' = convsep (matrix_blur _blur) (im_o != 0); + + im_s'' = im_blend im_s'.value im_s.value p_start?0; + im_o'' = im_blend im_o'.value im_o.value p_start?1; + + p_out = [im_s'', im_o'']; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Balance_apply_item = class + Menuaction "_Apply Values" + "apply scale and offset corrections, defined as image maps, to a given image" + /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an + * X-ray image an 32-bit float scale image and a 32-bit offset image. + */ + { + action im_in scale_im offset_im = class + Image value + { + _vislevel = 1; + + xfactor = im_in.width/scale_im.width; + yfactor = im_in.height/scale_im.height; + + _scale_im = resize Kernel_linear xfactor yfactor scale_im; + _offset_im = resize Kernel_linear xfactor yfactor offset_im; + + value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); + } + } +} + + Tilt_item = Filter_tilt_item; + + sep2 = Menuseparator; + + Rebuild_item = class + Menuaction "_Rebuild" + "disassemble mosaic, substitute image files and reassemble" { + action x = class + _result { + _vislevel = 3; + + old = String "In each filename, replace" "foo"; + new = String "With" "bar"; + + _result + = map_unary remosaic x + { + remosaic image + = Image (im_remosaic image.value old.value new.value); + } + } + } + + sep3 = Menuseparator; + +Clone_area_item = class + Menuaction "_Clone Area" + "replace dark or light section of im1 with pixels from im2" + { + action im1 im2 = class + _result { + _check_args = [ + [im1, "im1", check_Image], + [im2, "im2", check_Image] + ]; + _vislevel = 3; /* Region on first +image placed in the top left hand corner, + * positioned and size relative to the height and width of im1. + */ + r1 = Region_relative im1 0.05 0.05 0.05 0.05; + /* Mark on second image placed in the top left hand corner, + * positioned relative to the height and width of im2. Used to + * define _r2, the region from which the section of image is cloned + * from. + */ + p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left +p2.top r1.width r1.height; + mask = [r1 <= Options.sc, r1 >= +Options.sc]?(Options.replace); + Options = class + { + _vislevel = 3; + pause = Toggle "Pause process" true; + /* Option toggle used to define whether the user is + * replacing a dark or a light area. + */ + replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; + + // Used to select the area to be replaced. + sc = Scale "Scale cutoff" 0.01 mx (mx / 2) + {mx = Image_format.maxval im1.format;} + //Allows replacement with scale&offset balanced gaussian noise. + balance = Toggle "Balance cloned data to match surroundings." true; + //Allows replacement with scale&offset balanced + //gaussian noise. + process = Toggle "Replace area with Gaussian noise." false; + } + _result = im1, Options.pause + = Image (im_insert im1.value patch r1.left r1.top) + { r2 = Region im2 p2.left p2.top r1.width r1.height; + ref_meanmax = so_meanmax (if mask then 0 else r1); + mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], +[255, 255, 255]]; + mask_a = map_unary (dilate mask8) mask; + mask_b = convsep (matrix_blur 2) mask_a; + patch = so_balance ref_meanmax r1 r2 mask_b +Options.process, Options.balance + = so_balance ref_meanmax r1 r2 mask_b Options.process, +Options.process + = im_blend (get_image mask_b) (get_image r2) (get_image r1); + } + } + } +} + +Tasks_frame_item = Frame_item; + +Tasks_print_item = class + Menupullright "_Print" "useful stuff for image output" { + + Rotate_item = Image_transform_item.Rotate_item; + + Flip_item = Image_transform_item.Flip_item; + + Resize_item = Image_transform_item.Resize_item; + + Tone_item = Image_levels_item.Tone_item; + + Sharpen_item = class + Menuaction "_Sharpen" + "unsharp filter tuned for typical inkjet printers" { + action x = class + _result { + _vislevel = 3; + + target_dpi = Option "Sharpen for print at" [ + "400 dpi", + "300 dpi", + "150 dpi", + "75 dpi" + ] 1; + + _result + = map_unary process x + { + process image + = sharpen params?0 params?1 + params?2 params?3 params?4 params?5 + (colour_transform_to Image_type.LABQ image) + { + // sharpen params for various dpi + // just change the size of the area we search + param_table = [ + [7, 2.5, 40, 20, 0.5, 1.5], + [5, 2.5, 40, 20, 0.5, 1.5], + [3, 2.5, 40, 20, 0.5, 1.5], + [11, 2.5, 40, 20, 0.5, 1.5] + ]; + params = param_table?target_dpi; + } + } + } + } + + sep1 = Menuseparator; + + Temp_item = Colour_temperature_item; + + ICC_item = Colour_icc_item; +} diff --git a/share/nip2/compat/8.5/Widgets.def b/share/nip2/compat/8.5/Widgets.def new file mode 100644 index 00000000..c7ade22f --- /dev/null +++ b/share/nip2/compat/8.5/Widgets.def @@ -0,0 +1,46 @@ +Widget_slider_item = class + Menuaction "_Scale" "make a new scale widget" { + icon = "nip-slider-16.png"; + action = Scale "untitled scale" 0 255 128; +} + +Widget_toggle_item = class + Menuaction "_Toggle" "make a new toggle widget" { + action = Toggle "untitled toggle" false; +} + +Widget_option_item = class + Menuaction "_Option" "make a new option widget" { + action = Option "untitled option" ["option0", "option1"] 0; +} + +Widget_string_item = class + Menuaction "St_ring" "make a new string widget" { + action = String "Enter a string" "sample text"; +} + +Widget_number_item = class + Menuaction "_Number" "make a new number widget" { + action = Number "Enter a number" 42; +} + +Widget_expression_item = class + Menuaction "_Expression" "make a new expression widget" { + action = Expression "Enter an expression" 42; +} + +Widget_pathname_item = class + Menuaction "_File Chooser" "make a new file chooser widget" { + action = Pathname "Pick a file" + "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; +} + +Widget_font_item = class + Menuaction "F_ont Chooser" "make a new font chooser widget" { + action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; +} + +Widget_clock_item = class + Menuaction "_Clock" "make a new clock widget" { + action = Clock 1 1; +} diff --git a/share/nip2/compat/8.5/_Object.def b/share/nip2/compat/8.5/_Object.def new file mode 100644 index 00000000..9562f9b5 --- /dev/null +++ b/share/nip2/compat/8.5/_Object.def @@ -0,0 +1,364 @@ +/* Lots of little arg checks. Global for convenience. + */ + +check_any = [(const true), _ "any"]; +check_bool = [is_bool, _ "boolean"]; +check_real = [is_real, _ "real"]; +check_ureal = [is_ureal, _ "unsigned real"]; +check_preal = [is_preal, _ "positive real"]; +check_list = [is_list, _ "list"]; +check_real_list = [is_real_list, _ "list of real"]; +check_string = [is_string, _ "string"]; +check_string_list = [is_string_list, _ "list of string"]; +check_int = [is_int, _ "integer"]; +check_uint = [is_uint, _ "unsigned integer"]; +check_pint = [is_pint, _ "positive integer"]; +check_matrix = [is_matrix, _ "rectangular array of real"]; +check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; +check_image = [is_image, _ "image"]; +check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; +check_instance name = [is_instanceof name, name]; +check_Image = check_instance "Image"; +check_Matrix = [is_Matrix, _ "Matrix"]; +check_colour_space = [is_colour_space, + join_sep "|" Image_type.colour_spaces.names]; +check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; +check_Guide = [is_Guide, _ "HGuide|VGuide"]; +check_Colour = check_instance (_ "Colour"); +check_Mark = check_instance (_ "Mark"); + +/* Check a set of args to a class. Two members to look at: _check_args and + * _check_all. + * + * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] + * same number of lines as there are args + * + * stuff like "arg 2 must be real" + * + * - each line in _check_all is [test, "description"] + * any number of lines + * + * stuff like "to must be greater than from" + * + * generate an error dialog with a helpful message on failure. + * + * Have as a separate function to try to keep the size of _Object down. + */ +check_args x + = error message, badargs != [] || badalls != [] + = x +{ + argcheck = x._check_args; + allcheck = x._check_all; + + // indent string + indent = " "; + + // test for a condition in a check line fails + test_fail x = ! x?0; + + // set of failed argcheck indexes + badargs = map (extract 1) + (filter test_fail (zip2 (map testarg argcheck) [0..])) + { + testarg x = x?2?0 x?0; + } + + // set of failed allcheck indexes + badalls = map (extract 1) + (filter test_fail (zip2 (map hd allcheck) [0..])); + + // the error message + message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ + argmsg ++ allmsg ++ "\n" ++ + _ "where" ++ "\n" ++ arg_types ++ extra; + + // make the failed argcheck messages ... eg. ""value" should be + // real, you passed " etc. + argmsg = concat (map fmt badargs) + { + fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ + _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ + _ "you passed" ++ ":\n" ++ + indent ++ indent ++ print argcheck?n?0 ++ "\n"; + } + + // make the failed allcheck messages ... eg "condition failed: + // x < y" ... don't make a message if any typechecks have + // failed, as we'll probably error horribly + allmsg + = [], badargs != [] + = concat (map fmt badalls) ++ + _ "you passed" ++ "\n" ++ + concat (map fmt_arg argcheck) + { + fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; + fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; + } + + // make arg type notes + arg_types = join_sep "\n" (map fmt argcheck) + { + fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; + } + + // extra bit at the bottom, if we have any conditions + extra + = [], allcheck == [] + = "\n" ++ _ "and" ++ "\n" ++ all_desc; + + // make a list of all the allcheck descriptions, with a few + // spaces in front + all_desc_list = map (join indent @ extract 1) allcheck; + + // join em up to make a set of condition notes + all_desc = join_sep "\n" all_desc_list; +} + +/* Operator overloading stuff. + */ + +Operator_type = class { + ARITHMETIC = 1; // eg. add + RELATIONAL = 2; // eg. less + COMPOUND = 3; // eg. max/mean/etc. + COMPOUND_REWRAP = 4; // eg. transpose +} + +Operator op_name fn type symmetric = class { +} + +/* Form the converse of an Operator. + */ +oo_converse op + = Operator (converse_name op.op_name) + (converse op.fn) op.type op.symmetric +{ + converse_name x + = init x, last x == last "'" + = x ++ "'"; +} + +/* Given an operator name, look up the definition. + */ +oo_binary_lookup op_name + = matches?0, matches != [] + = error (_ "unknown binary operator" ++ ": " ++ print op_name) +{ + operator_table = [ + Operator "add" add + Operator_type.ARITHMETIC true, + Operator "subtract" subtract + Operator_type.ARITHMETIC false, + Operator "remainder" remainder + Operator_type.ARITHMETIC false, + Operator "power" power + Operator_type.ARITHMETIC false, + Operator "subscript" subscript + Operator_type.ARITHMETIC false, + Operator "left_shift" left_shift + Operator_type.ARITHMETIC false, + Operator "right_shift" right_shift + Operator_type.ARITHMETIC false, + Operator "divide" divide + Operator_type.ARITHMETIC false, + Operator "join" join + Operator_type.ARITHMETIC false, + Operator "multiply" multiply + Operator_type.ARITHMETIC true, + Operator "logical_and" logical_and + Operator_type.ARITHMETIC true, + Operator "logical_or" logical_or + Operator_type.ARITHMETIC true, + Operator "bitwise_and" bitwise_and + Operator_type.ARITHMETIC true, + Operator "bitwise_or" bitwise_or + Operator_type.ARITHMETIC true, + Operator "eor" eor + Operator_type.ARITHMETIC true, + Operator "comma" comma + Operator_type.ARITHMETIC false, + Operator "if_then_else" if_then_else + Operator_type.ARITHMETIC false, + Operator "equal" equal + Operator_type.RELATIONAL true, + Operator "not_equal" not_equal + Operator_type.RELATIONAL true, + Operator "less" less + Operator_type.RELATIONAL false, + Operator "less_equal" less_equal + Operator_type.RELATIONAL false + ]; + + matches = filter test_name operator_table; + test_name x = x.op_name == op_name; +} + +/* Given an operator name, look up a function that implements that + * operator. + */ +oo_unary_lookup op_name + = matches?0, matches != [] + = error (_ "unknown unary operator" ++ ": " ++ print op_name) +{ + operator_table = [ + /* Operators. + */ + Operator "cast_signed_char" cast_signed_char + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_char" cast_unsigned_char + Operator_type.ARITHMETIC false, + Operator "cast_signed_short" cast_signed_short + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_short" cast_unsigned_short + Operator_type.ARITHMETIC false, + Operator "cast_signed_int" cast_signed_int + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_int" cast_unsigned_int + Operator_type.ARITHMETIC false, + Operator "cast_float" cast_float + Operator_type.ARITHMETIC false, + Operator "cast_double" cast_double + Operator_type.ARITHMETIC false, + Operator "cast_complex" cast_complex + Operator_type.ARITHMETIC false, + Operator "cast_double_complex" cast_double_complex + Operator_type.ARITHMETIC false, + Operator "unary_minus" unary_minus + Operator_type.ARITHMETIC false, + Operator "negate" negate + Operator_type.RELATIONAL false, + Operator "complement" complement + Operator_type.ARITHMETIC false, + Operator "unary_plus" unary_plus + Operator_type.ARITHMETIC false, + + /* Built in projections. + */ + Operator "re" re Operator_type.ARITHMETIC false, + Operator "im" im Operator_type.ARITHMETIC false, + Operator "hd" hd Operator_type.ARITHMETIC false, + Operator "tl" tl Operator_type.ARITHMETIC false, + + /* Maths builtins. + */ + Operator "sin" sin Operator_type.ARITHMETIC false, + Operator "cos" cos Operator_type.ARITHMETIC false, + Operator "tan" tan Operator_type.ARITHMETIC false, + Operator "asin" asin Operator_type.ARITHMETIC false, + Operator "acos" acos Operator_type.ARITHMETIC false, + Operator "atan" atan Operator_type.ARITHMETIC false, + Operator "log" log Operator_type.ARITHMETIC false, + Operator "log10" log10 Operator_type.ARITHMETIC false, + Operator "exp" exp Operator_type.ARITHMETIC false, + Operator "exp10" exp10 Operator_type.ARITHMETIC false, + Operator "ceil" ceil Operator_type.ARITHMETIC false, + Operator "floor" floor Operator_type.ARITHMETIC false + ]; + + matches = filter test_name operator_table; + test_name x = x.op_name == op_name; +} + +/* Find the matching methods in a method table. + */ +oo_method_lookup table = map (extract 0) (filter (extract 1) table); + +/* A binary op: a is a class, b may be a class ... eg. "add" a b + + two obvious ways to find a method: + + - a.oo_binary_search "add" (+) b + - b.oo_binary_search "add'" (converse (+)) a, is_class b + + if these fail but op is a symmetric operator (eg. a + b == b + a), we can + also try reversing the args + + - a.oo_binary_search "add'" (converse (+)) b + - b.oo_binary_search "add" (+) a, is_class b + + if those fail as well, but this is ==, do pointer equals as a fallback + + */ +oo_binary_function op a b + = matches1?0, + matches1 != [] + = matches2?0, + is_class b && matches2 != [] + = matches3?0, + op.symmetric && matches3 != [] + = matches4?0, + op.symmetric && is_class b && matches4 != [] + = pointer_equal a b, + op.op_name == "equal" || op.op_name == "equal'" + = not_pointer_equal a b, + op.op_name == "not_equal" || op.op_name == "not_equal'" + = error (_ "No method found for binary operator." ++ "\n" ++ + _ "left" ++ " = " ++ print a ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "right" ++ " = " ++ print b) +{ + matches1 = oo_method_lookup (a.oo_binary_table op b); + matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); + matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); + matches4 = oo_method_lookup (b.oo_binary_table op a); +} + +/* A binary op: a is not a class, b is a class ... eg. "subtract" a b + + only one way to find a method: + + - b.oo_binary_search "subtract'" (converse (-)) a + + if this fails but op is a symmetric operator (eg. a + b == b + a), we can + try reversing the args + + - b.oo_binary_search "add" (+) a, is_class b + + if that fails as well, but this is ==, do pointer equals as a fallback + + */ +oo_binary'_function op a b + = matches1?0, matches1 != [] + = matches2?0, op.symmetric && matches2 != [] + = pointer_equal a b, + op.op_name == "equal" || op.op_name == "equal'" + = not_pointer_equal a b, + op.op_name == "not_equal" || op.op_name == "not_equal'" + = error (_ "No method found for binary operator." ++ "\n" ++ + _ "left" ++ " = " ++ print a ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "right" ++ " = " ++ print b) +{ + matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); + matches2 = oo_method_lookup (b.oo_binary_table op a); +} + +oo_unary_function op x + = matches?0, matches != [] + = error (_ "No method found for unary operator." ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "argument" ++ " = " ++ print x) +{ + matches = oo_method_lookup (x.oo_unary_table op); +} + +/* Base class for nip's built-in classes ... base check function, base + * operator overload functions. + */ +_Object = class { + check = check_args this; + + // these should always be defined + _check_args = []; + _check_all = []; + + /* Operator overloading stuff. + */ + oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; + oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; + oo_unary op = oo_unary_function (oo_unary_lookup op) this; + + oo_binary_table op x = []; + oo_unary_table op = []; +} diff --git a/share/nip2/compat/8.5/_convert.def b/share/nip2/compat/8.5/_convert.def new file mode 100644 index 00000000..c8002860 --- /dev/null +++ b/share/nip2/compat/8.5/_convert.def @@ -0,0 +1,685 @@ + +/* Try to make a Matrix ... works for Vector/Image/Real, plus image/real + */ +to_matrix x + = to_matrix x.expr, is_Expression x + = x, is_Matrix x + = oo_unary_function to_matrix_op x, is_class x + = tom x +{ + to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; + + tom x + = Matrix (itom x), is_image x + = Matrix [[x]], is_real x + = Matrix [x], is_real_list x + = Matrix x, is_matrix x + = error (_ "bad arguments to " ++ "to_matrix"); + + itom i + = (im_vips2mask ((double) i)).value, is_image i + = error (_ "not image"); +} + +/* Try to make a Vector ... works for Vector/Image/Real, plus image/real + */ +to_vector x + = to_vector x.expr, is_Expression x + = x, is_Vector x + = oo_unary_function to_vector_op x, is_class x + = tov x +{ + to_vector_op = Operator "to_vector" tov Operator_type.COMPOUND false; + + tov x + = Vector (itov x), is_image x + = Vector [x], is_real x + = Vector x, is_real_list x + = Vector x?0, is_matrix x && len x == 1 + = Vector (transpose x)?0, is_matrix x && len x?0 == 1 + = error (_ "bad arguments to " ++ "to_vector"); + + itov i + = v, is_image i + = error (_ "not image") + { + m = im_vips2mask ((double) i); + v + = m.value?0, m.height == 1 + = (transpose m.value)?0, m.width == 1 + = error (_ "image is not 1xN or Nx1"); + } +} + +/* Try to make an Image ... works for Vector/Matrix/Real, plus image/real + * Special case for Colour ... pull out the colour_space and set Type in the + * image. + */ +to_image x + = to_image x.expr, is_Expression x + = Image x.value, is_Plot x + = x, is_Image x + = Image (image_set_type + (Image_type.colour_spaces.lookup 0 1 x.colour_space) + (mtoi [x.value])), + is_Colour x + = oo_unary_function to_image_op x, is_class x + = toi x +{ + to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; + + toi x + = Image x, is_image x + = Image (mtoi [[x]]), is_real x + = Image (mtoi [x]), is_real_list x + = Image (mtoi x), is_matrix x + = error (_ "bad arguments to " ++ "to_image"); + + // [[real]] -> image + mtoi m + = im_mask2vips (Matrix m), width != 3 + = joinup (im_mask2vips (Matrix m)) + { + width = len m?0; + height = len m; + joinup i + = b1 ++ b2 ++ b3 + { + b1 = extract_area 0 0 1 height i; + b2 = extract_area 1 0 1 height i; + b3 = extract_area 2 0 1 height i; + } + } +} + +// like to_image, but we do 1x1 pixel + x, then embed it up +// always make an unwrapped image for speed ... this gets used by ifthenelse +// and stuff like that +// format can be NULL, meaning set format from x +to_image_size width height bands format x + = x, is_image x + = x.value, is_Image x + = im'' +{ + // we want x to set the target format if we don't have one, so we + // can't use image_new + im = im_black 1 1 bands + x; + im' + = clip2fmt format im, format != NULL + = im; + im'' = embed 1 0 0 width height im'; +} + +/* Try to make a Colour. + */ +to_colour x + = to_colour x.expr, is_Expression x + = x, is_Colour x + = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x + = oo_unary_function to_colour_op x, is_class x + = toc x +{ + to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; + + toc x + = Colour (colour_space (get_type x)) + (map mean (bandsplit (get_image x))), + has_image x && has_type x + = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono + = Colour "sRGB" x, is_real_list x && is_list_len 3 x + = map toc x, is_matrix x + = error (_ "bad arguments to " ++ "to_colour"); + + colour_space type + = table.get_name type, table.has_name type + = error (_ "unable to make Colour from " ++ table.get_name type ++ + _ " image") + { + table = Image_type.colour_spaces; + } +} + +/* Try to make a real. (not a Real!) + */ +to_real x + = to_real x.expr, is_Expression x + = oo_unary_function to_real_op x, is_class x + = tor x +{ + to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; + + tor x + = x, is_real x + = abs x, is_complex x + = 1, is_bool x && x + = 0, is_bool x && !x + = error (_ "bad arguments to " ++ "to_real"); +} + +to_int x = (int) (to_real x); + +/* Try to make a list ... ungroup, basically. We remove the innermost layer of + * Groups. + */ +to_list x + = x.value, is_Group x && !contains_Group x.value + = Group (map to_list x.value), is_Group x + = x; + +/* Try to make a group. The outermost list objects become Group()'d. + */ +to_group x + = Group x, is_list x + = Group (map to_group x.value), is_Group x + = x; + +/* Parse a positive integer. + */ +parse_pint l + = foldl acc 0 l +{ + acc sofar ch = sofar * 10 + parse_c ch; + + /* Turn a char digit to a number. + */ + parse_c ch + = error (_ "not a digit"), !is_digit ch + = (int) ch - (int) '0'; +} + +/* Parse an integer, with an optional sign character. + */ +parse_int l + = error (_ "badly formed number"), !is_list_len 2 parts + = sign * n +{ + parts = splitpl [member "+-", is_digit] l; + + n = parse_pint parts?1; + sign + = 1, parts?0 == [] || parts?0 == "+" + = -1; +} + +/* Parse a float. + * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? + */ +parse_float l + = err, !is_list_len 4 parts + = sign * (abs ipart + fpart) * 10 ** exp +{ + err = error (_ "badly formed number"); + + parts = splitpl [ + member "+-0123456789", + member ".0123456789", + member "eE", + member "+-0123456789" + ] l; + + ipart = parse_int parts?0; + sign + = 1, ipart >= 0 + = -1; + fpart + = 0, parts?1 == []; + = err, parts?1?0 != '.' + = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); + exp + = 0, parts?2 == [] && parts?3 == [] + = err, parts?2 == [] + = parse_int parts?3; + +} + +/* Parse a time in "hh:mm:ss" into seconds. + +We could do this in one line :) + + = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map + parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, + equal ':', is_digit] l))) [0,2,4]; + +but it's totally unreadable. + + */ +parse_time l + = error (_ "badly formed time"), !is_list_len 5 parts + = s + 60 * m + 60 * 60 * h +{ + parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; + h = parse_int parts?0; + m = parse_int parts?2; + s = parse_int parts?4; +} + +/* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by + * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix + */ +D652D50_direct = Matrix + [[ 1.13529, -0.0604663, -0.0606321 ], + [ 0.0975399, 0.935024, -0.0256156 ], + [ -0.0336428, 0.0414702, 0.994135 ]]; + +D502D65_direct = D652D50_direct ** -1; + +/* Convert normalised XYZ to bradford RGB. + */ +XYZ2RGBbrad = Matrix + [[0.8951, 0.2664, -0.1614], + [-0.7502, 1.7135, 0.0367], + [0.0389, -0.0685, 1.0296]]; + +/* Convert bradford RGB to normalised XYZ. + */ +RGBbrad2XYZ = XYZ2RGBbrad ** -1; + +D93_whitepoint = Vector [89.7400, 100, 130.7700]; +D75_whitepoint = Vector [94.9682, 100, 122.5710]; +D65_whitepoint = Vector [95.0470, 100, 108.8827]; +D55_whitepoint = Vector [95.6831, 100, 92.0871]; +D50_whitepoint = Vector [96.4250, 100, 82.4680]; +A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K +B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K +C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K +E_whitepoint = Vector [100, 100, 100]; // ill. free +D3250_whitepoint = Vector [105.6590, 100, 45.8501]; + +Whitepoints = Enum [ + $D93 => D93_whitepoint, + $D75 => D75_whitepoint, + $D65 => D65_whitepoint, + $D55 => D55_whitepoint, + $D50 => D50_whitepoint, + $A => A_whitepoint, + $B => B_whitepoint, + $C => C_whitepoint, + $E => E_whitepoint, + $D3250 => D3250_whitepoint +]; + +/* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. + */ +im_D502D65 xyz + = xyz''' +{ + xyz' = xyz / D50_whitepoint; + + rgb = recomb XYZ2RGBbrad xyz'; + + // move white in bradford RGB + rgb' = rgb / Vector [0.94, 1.02, 1.33]; + + xyz'' = recomb RGBbrad2XYZ rgb'; + + // back to D65 + xyz''' = xyz'' * D65_whitepoint; +} + +/* Convert D65 XYZ to D50 using the bradford approx. + */ +im_D652D50 xyz + = xyz''' +{ + xyz' = xyz / D65_whitepoint; + + rgb = recomb XYZ2RGBbrad xyz'; + + // move white in bradford RGB + rgb' = rgb * Vector [0.94, 1.02, 1.33]; + + xyz'' = recomb RGBbrad2XYZ rgb'; + + xyz''' = xyz'' * D50_whitepoint; +} + +/* Convert D50 XYZ to Lab. + */ +im_D50XYZ2Lab xyz + = im_XYZ2Lab_temp xyz + D50_whitepoint.value?0 + D50_whitepoint.value?1 + D50_whitepoint.value?2; +im_D50Lab2XYZ lab + = im_Lab2XYZ_temp lab + D50_whitepoint.value?0 + D50_whitepoint.value?1 + D50_whitepoint.value?2; + +/* ... and mono conversions + */ +im_sRGB2mono in + = (image_set_type Image_type.B_W @ + clip2fmt (get_header "BandFmt" in) @ + recomb (Matrix [[.3, .6, .1]])) in; +im_mono2sRGB in + = image_set_type Image_type.sRGB (in ++ in ++ in); + +im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; + +im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; + +// from the 16 bit RGB and GREY formats +im_1628 x = im_clip (x >> 8); +im_162f x = x / 256; + +im_8216 x = (im_clip2us x) << 8; +im_f216 x = im_clip2us (x * 256); + +im_RGB162GREY16 in + = (image_set_type Image_type.GREY16 @ + clip2fmt (get_header "BandFmt" in) @ + recomb (Matrix [[.3, .6, .1]])) in; +im_GREY162RGB16 in + = image_set_type Image_type.RGB16 (in ++ in ++ in); + +/* apply a func to an image ... make it 1 or 3 bands, and reapply other bands + * on the way out. Except if it's LABPACK. + */ +colour_apply fn x + = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK + = x'' +{ + b = get_bands x; + c = get_coding x; + + first + = extract_bands 0 3 x, b > 3 + = extract_bands 0 1 x; + tail + = extract_bands 3 (b - 3) x, b > 3 + = extract_bands 1 (b - 1) x; + x' = fn first; + x'' = x' ++ clip2fmt (get_format x') tail; +} + +/* Any 1-ary colour op, applied to Vector/Image/Matrix or image + */ +colour_unary fn x + = oo_unary_function colour_op x, is_class x + = colour_apply fn x, is_image x + = colour_apply fn [x], is_real x + = error (_ "bad arguments to " ++ "colour_unary") +{ + // COMPOUND_REWRAP ... signal to the colour class to go to image and + // back + colour_op = Operator "colour_unary" + colour_object Operator_type.COMPOUND_REWRAP false; + + colour_object x + = colour_real_list x, is_real_list x + = map colour_real_list x, is_matrix x + = colour_apply fn x, is_image x + = error (_ "bad arguments to " ++ "colour_unary"); + + colour_real_list l + = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; +} + +/* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... + * name is op name for error messages etc. + */ +colour_binary name fn x y + = oo_binary_function colour_op x y, is_class x + = oo_binary'_function colour_op x y, is_class y + = fn x y, is_image x && is_image y + = error (_ "bad arguments to " ++ name) +{ + colour_op = Operator name + colour_object Operator_type.COMPOUND_REWRAP true; + + colour_object x y + = fn x y, is_image x && is_image y + = colour_real_list fn x y, is_real_list x && is_real_list y + = map (colour_real_list fn x) y, is_real_list x && is_matrix y + = map (colour_real_list (converse fn) y) x, + is_matrix x && is_real_list y + = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y + = error (_ "bad arguments to " ++ name); + + colour_real_list fn l1 l2 + = (to_matrix (fn i1 i2)).value?0 + { + i1 = (float) (to_image (Vector l1)).value; + i2 = (float) (to_image (Vector l2)).value; + } +} + +_colour_conversion_table = [ + /* Lines are [space-from, space-to, conversion function]. Could do + * this as a big array, but table lookup feels safer. + */ + [B_W, B_W, image_set_type B_W], + [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, sRGB, im_mono2sRGB @ im_clip], + [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], + [B_W, GREY16, image_set_type GREY16 @ im_8216], + [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ + im_mono2sRGB @ im_clip], + + [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], + [XYZ, XYZ, image_set_type XYZ], + [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], + [XYZ, LAB, im_XYZ2Lab @ im_clip2f], + [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], + [XYZ, UCS, im_XYZ2UCS @ im_clip2f], + [XYZ, RGB, im_XYZ2disp @ im_clip2f], + [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], + [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], + [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], + + [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], + [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], + [YXY, YXY, image_set_type YXY], + [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], + [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], + [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], + [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ + im_clip2f], + + [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], + [LAB, XYZ, im_Lab2XYZ @ im_clip2f], + [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], + [LAB, LAB, image_set_type LAB @ im_clip2f], + [LAB, LCH, im_Lab2LCh @ im_clip2f], + [LAB, UCS, im_Lab2UCS @ im_clip2f], + [LAB, RGB, im_Lab2disp @ im_clip2f], + [LAB, sRGB, im_Lab2sRGB @ im_clip2f], + [LAB, LABQ, im_Lab2LabQ @ im_clip2f], + [LAB, LABS, im_Lab2LabS @ im_clip2f], + + [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], + [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], + [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], + [LCH, LAB, im_LCh2Lab @ im_clip2f], + [LCH, LCH, image_set_type LCH], + [LCH, UCS, im_LCh2UCS @ im_clip2f], + [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], + [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], + [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], + [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], + + [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], + [UCS, XYZ, im_UCS2XYZ @ im_clip2f], + [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], + [UCS, LAB, im_UCS2Lab @ im_clip2f], + [UCS, LCH, im_UCS2LCh @ im_clip2f], + [UCS, UCS, image_set_type UCS], + [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], + [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], + [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], + [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], + + [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], + [RGB, XYZ, im_disp2XYZ @ im_clip], + [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], + [RGB, LAB, im_disp2Lab @ im_clip], + [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], + [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], + [RGB, RGB, image_set_type RGB], + [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], + [RGB, RGB16, image_set_type RGB16 @ im_8216], + [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], + [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], + [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], + + [sRGB, B_W, im_sRGB2mono], + [sRGB, XYZ, im_sRGB2XYZ @ im_clip], + [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], + [sRGB, LAB, im_sRGB2Lab @ im_clip], + [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], + [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], + [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], + [sRGB, sRGB, image_set_type sRGB], + [sRGB, RGB16, image_set_type RGB16 @ im_8216], + [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], + [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], + [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], + + [RGB16, B_W, im_1628 @ im_sRGB2mono], + [RGB16, RGB, image_set_type RGB @ im_1628], + [RGB16, sRGB, image_set_type sRGB @ im_1628], + [RGB16, RGB16, image_set_type RGB16], + [RGB16, GREY16, im_RGB162GREY16], + [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], + + [GREY16, B_W, image_set_type B_W @ im_1628], + [GREY16, RGB, im_mono2sRGB @ im_1628], + [GREY16, sRGB, im_mono2sRGB @ im_1628], + [GREY16, RGB16, im_GREY162RGB16], + [GREY16, GREY16, image_set_type GREY16], + + [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], + [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], + [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], + [LABQ, LAB, im_LabQ2Lab], + [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], + [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], + [LABQ, RGB, im_LabQ2disp], + [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], + [LABQ, LABQ, image_set_type LABQ], + [LABQ, LABS, im_LabQ2LabS], + + [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ + im_LabS2LabQ @ im_clip2s], + [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, YXY, im_XYZ2Yxy @ + im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, LAB, im_LabS2Lab], + [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], + [LABS, sRGB, im_XYZ2sRGB @ + im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, LABQ, im_LabS2LabQ @ im_clip2s], + [LABS, LABS, image_set_type LABS] +] +{ + /* From Image_type ... repeat here for brevity. Use same ordering as + * in Colour menu for consistency. + */ + B_W = 1; + XYZ = 12; + YXY = 23; + LAB = 13; + LCH = 19; + UCS = 18; + RGB = 17; + sRGB = 22; + RGB16 = 25; + GREY16 = 26; + LABQ = 16; + LABS = 21; +} + +/* Transform between two colour spaces. + */ +colour_transform from to in + = colour_unary _colour_conversion_table?i?2 in, i >= 0 + = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ + _ " to " ++ Image_type.type_names.get_name to) +{ + match x = x?0 == from && x?1 == to; + i = index match _colour_conversion_table; +} + +/* Transform to a colour space, assuming the type field in the input is + * correct + */ +colour_transform_to to in = colour_transform (get_type in) to in; + +/* String for path separator on this platform. + */ +path_separator = expand "$SEP"; + +/* Form a relative pathname. + * path_relative ["home", "john"] == "home/john" + * path_relative [] == "" + */ +path_relative l = join_sep path_separator l; + +/* Form an absolute pathname. + * path_absolute ["home", "john"] == "/home/john" + * path_absolute [] == "/" + * If the first component looks like 'A:', don't add an initial separator. + */ +path_absolute l + = path_relative l, + len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' + = path_separator ++ path_relative l; + +/* Parse a pathname. + * path_parse "/home/john" == ["home", "john"] + * path_parse "home/john" == ["home", "john"] + */ +path_parse str + = split (equal path_separator?0) str; + +/* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] + */ +system_search_path + = [vipsbin] ++ + map path_parse (split (equal path_sep) (expand "$PATH")) +{ + /* On some platforms we ship vips with a few extra progs. Search + * $VIPSHOME/bin first. + */ + vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; + + path_sep + = ':', expand "$SEP" == "/" + = ';'; +} + +/* Search $PATH for the first occurence of name, or "". + */ +search_for name + = hits?0, hits != [] + = "" +{ + exe_name = name ++ expand "$EXEEXT"; + form_path p = path_absolute (p ++ [exe_name]); + paths = map form_path system_search_path; + hits = dropwhile (equal []) (map search paths); +} + +/* Search $PATH for the first occurence of name, error on failure. + */ +search_for_error name + = path, path != "" + = error (exe_name ++ " not found on your search path. " ++ + "Check you have installed the program and it is on your PATH.") +{ + exe_name = name ++ expand "$EXEEXT"; + path = search_for name; +} + diff --git a/share/nip2/compat/8.5/_generate.def b/share/nip2/compat/8.5/_generate.def new file mode 100644 index 00000000..1ce3af2a --- /dev/null +++ b/share/nip2/compat/8.5/_generate.def @@ -0,0 +1,155 @@ + +/* make an image of size x by y whose pixels are their coordinates. + */ +make_xy x y = im_make_xy (to_real x) (to_real y); + +/* make an image with the specified properties ... pixel is (eg.) + * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and + * type, generate a 3 band float image, and lab2labq it before handing it + * back. + */ +image_new w h b fmt coding type pixel xoff yoff + = embed 1 0 0 w h im'''' +{ + b' + = 3, coding == Image_coding.LABPACK + = b; + fmt' + = Image_format.FLOAT, coding == Image_coding.LABPACK + = fmt; + type' + = Image_type.LAB, coding == Image_coding.LABPACK + = type; + + im = im_black 1 1 (to_real b') + pixel; + + im' = clip2fmt fmt' im; + + im'' + = im_Lab2LabQ im', coding == Image_coding.LABPACK; + = im'; + + im''' = image_set_type type' im''; + im'''' = image_set_origin xoff yoff im'''; +} + +mkim options x y b + = Image (image_new x y b + (opt $format) (opt $coding) (opt $type) + (opt $pixel) + (opt $xoffset) (opt $yoffset)) +{ + opt = get_option options [ + $format => Image_format.UCHAR, + $coding => Image_coding.NOCODING, + $type => Image_type.sRGB, + $pixel => 0, + $xoffset => 0, + $yoffset => 0 + ]; +} + +/* generate a slice of LAB space size x size pixels for L* == l + */ +lab_slice size l + = image_set_type Image_type.LAB im +{ + L = image_new size size 1 + Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; + A1 = im_fgrey (to_real size) (to_real size); + + /* im_fgrey always makes 0-1, so these ranges can be wired in. + */ + A2 = A1 * 256 - 128; + + A4 = im_rot90 A2; + im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); +} + +/* Look at Image, try to make a Colour (failing that, a Vector) which is white + * for that image type. + */ +image_white im + = colour_transform_to type white_lab, + bands == 3 && coding == Image_coding.NOCODING && + colour_spaces.present 1 type + = white_lab, + coding == Image_coding.LABPACK + = Vector (replicate bands (max_value.lookup 1 0 format)) +{ + bands = im.bands; + type = im.type; + format = im.format; + coding = im.coding; + colour_spaces = Image_type.colour_spaces; + + // white as LAB + white_lab = Colour "Lab" [100, 0, 0]; + + // maximum value for this numeric type + max_value = Table [ + [255, Image_format.DPCOMPLEX], + [255, Image_format.DOUBLE], + [255, Image_format.COMPLEX], + [255, Image_format.FLOAT], + [2 ** 31 - 1, Image_format.INT], + [2 ** 32 - 1, Image_format.UINT], + [2 ** 15 - 1, Image_format.SHORT], + [2 ** 16 - 1, Image_format.USHORT], + [2 ** 7 - 1, Image_format.CHAR], + [2 ** 8 - 1, Image_format.UCHAR] + ]; +} + +/* Make a seperable gaussian mask. + */ +matrix_gaussian_blur radius + = im_gauss_imask_sep (radius / 3) 0.2; + +/* Make a seperable square mask. + */ +matrix_blur radius + = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] +{ + mask_sq_line = replicate (2 * radius - 1) 1; +} + +/* Make a colour from a temperature. + */ +colour_from_temp T + = error (_ "T out of range"), T < 1667 || T > 25000 + = Colour "Yxy" [50, x, y] +{ + // Kim et all approximation + // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation + x + = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 + = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + + 0.2226347 * 10 ** 3 / T + 0.240390; + + y + = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + + 2.18555832 * x - 0.20219638, T < 2222 + = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + + 2.09137015 * x - 0.16748867, T < 4000 + = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + + 3.75112997 * x - 0.37001483; +} + +temp_from_colour z + = T +{ + c = colour_transform_to Image_type.YXY (to_colour z); + x = c.value?1; + y = c.value?2; + + // McCamy's approximation, see eg. + // http://en.wikipedia.org/wiki/Color_temperature#Approximation + + xe = 0.332; + ye = 0.1858; + n = (x - xe) / (y - ye); + T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; +} + diff --git a/share/nip2/compat/8.5/_joe_extra.def b/share/nip2/compat/8.5/_joe_extra.def new file mode 100644 index 00000000..0277d261 --- /dev/null +++ b/share/nip2/compat/8.5/_joe_extra.def @@ -0,0 +1,470 @@ +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Frame_item = class + Menupullright "Picture _Frame" "working with images of frames" + { + //////////////////////////////////////////////////////////////////////////////////// + Build_frame_item = class + Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" + { + //////////////////////////////////////////////////////////////////////////////////// + Frame_corner_item = class + Menuaction "_Frame Corner" + "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" + { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 0; + + _type = Image_type.colour_spaces.get_name b.type; + + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + //Scale frame image if required. + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.mount_colour.value, mount_options.apply + = 0; + + //Produce scaled and resized frame. + frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Simple_frame_item = class + Menuaction "_Simple Frame" + "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" + { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 0; + + _type = Image_type.colour_spaces.get_name b.type; + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + //Scale frame image if required. + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.mount_colour.value, mount_options.apply + = 0; + + //Produce scaled and resized frame. + frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Complex_frame_item = class + Menuaction "_Complex Frame" + "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 1; + + _type = Image_type.colour_spaces.get_name b.type; + + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _es = variables.edge_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.colour.value, mount_options.apply + = 0; + + + //Produce scaled and resized frame. + frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + } +//////////////////////////////////////////////////////////////////////////////////// + Straighten_frame_item = class + Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { + action a = Perspective_item.action a; + } +}; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Select_item = class + Menupullright "_Select" + "select user defined areas of an image" { + prefs = Workspaces.Preferences; + + /* Option toggle used to define whether the user is replacing a + * dark or a light area. + */ + _control = Option "Make" [ + "Selection Brighter", + "Selection Darker", + "Selection Black", + "Selection White", + "Background Black", + "Background White", + "Mask" + ] 4; + + control_selection mask im no + = [ + if mask then im * 1.2 else im * 1, + if mask then im * 0.8 else im * 1, + if mask then 0 else im, + if mask then 255 else im, + if mask then im else 0, + if mask then im else 255, + mask + ]?no; + + Rectangle = class + Menuaction "_Rectangle" + "use an Arrow or Region x to define a rectangle" + { + action x = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + im = x.image; + mask = Image m + { + rx + = x.region_rect, is_Region x + = x; + b = image_new im.width im.height 1 0 0 1 0 0 0; + w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; + m = insert_noexpand rx.nleft rx.ntop w b; + } + } + } + } + + Elipse = class + Menuaction "_Ellipse" + "use a line/arrow x to define the center point radius and direction of an ellipse" + { + action x = class + _result { + _vislevel = 3; + + control = _control; + width = Scale "Width" 0.01 1 0.5; + + _result = control_selection mask im control + { + mask = select_ellipse x width.value; + im = x.image; + } + } + } + + Tetragon = class + Menuaction "_Tetragon" + "selects the convex area defined by four points" + { + action a b c d = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + mask = select_tetragon a b c d; + im = get_image a; + } + } + + } + + Polygon = class + Menuaction "_Polygon" + "selects a polygon from an ordered group of points" + { + action pt_list = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + mask = select_polygon pt_list; + im = get_image ((pt_list.value)?0); + } + } + } + + sep1 = Menuseparator; + + Threshold_item = class + Menuaction "Thres_hold" "simple image threshold" { + action x = class + _result { + _vislevel = 3; + + t + = Scale "Threshold" 0 mx (mx / 2) + { + mx + = Image_format.maxval x.format, is_Image x + = 255; + } + + _result = map_unary (more t.value) x; + } + } + + Threshold_percent_item = class + Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { + action x = class + _result { + _vislevel = 3; + + t = Scale "Percentage of pixels" 0 100 50; + + _result + = map_unary (more (hist_thresh (t.value / 100) x)) x; + } + } + + sep2 = Menuseparator; + + Segment_item = class + Menuaction "_Segment" "break image into disjoint regions" { + action x = class + _result { + _vislevel = 3; + + segments + = Expression "Number of disjoint regions" + (map_unary (get_header "n-segments") _result); + + _result = map_unary segment x; + } + } + + }; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +Perspective_match_item = class + Menuaction "_Perspective Match" + "rotate, scale and skew one image to match another" { + action x y = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + _b = find_image y; + + ap1 = Mark_relative _a 0.1 0.1; + ap2 = Mark_relative _a 0.9 0.1; + ap3 = Mark_relative _a 0.1 0.9; + ap4 = Mark_relative _a 0.9 0.9; + + bp1 = Mark_relative _b 0.1 0.1; + bp2 = Mark_relative _b 0.9 0.1; + bp3 = Mark_relative _b 0.1 0.9; + bp4 = Mark_relative _b 0.9 0.9; + + _result = map_binary process x y + { + f1 = _a.width / _b.width; + f2 = _a.height / _b.height; + + rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; + pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; + + to = [ + rl?0.left, rl?0.top, + rl?1.left, rl?1.top, + rl?2.left, rl?2.top, + rl?3.left, rl?3.top + ]; + + from = [ + pl?0.left * f1, pl?0.top * f2, + pl?1.left * f1, pl?1.top * f2, + pl?2.left * f1, pl?2.top * f2, + pl?3.left * f1, pl?3.top * f2 + ]; + + trans = perspective_transform to from; + + process a b = transform 1 0 trans b2 + { + b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) + = resize Kernel_linear f1 1 b1 + {b1 = resize Kernel_linear 1 f2 b;} + } + } + } + }; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Perspective_item = class + Menuaction "Pe_rspective Distort" + "rotate, scale and skew an image with respect to defined points" { + action x = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + + dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; + ap1 = Mark_relative _a 0.1 0.1; + ap2 = Mark_relative _a 0.9 0.1; + ap3 = Mark_relative _a 0.9 0.9; + ap4 = Mark_relative _a 0.1 0.9; + + _result = map_unary process x + { + trans = [perspective_transform to from, perspective_transform from to]?(dir.value) + { + rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; + to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, + (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; + from=[0, 0, (_a.width - 1), 0, + (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; + } + + process a = transform 1 0 trans a; + } + } + }; + diff --git a/share/nip2/compat/8.5/_joe_utilities.def b/share/nip2/compat/8.5/_joe_utilities.def new file mode 100644 index 00000000..be7931df --- /dev/null +++ b/share/nip2/compat/8.5/_joe_utilities.def @@ -0,0 +1,705 @@ +/* ******Functions included in start/_NG_utilities.def:****** + * + * so_balance ref_meanmax im1 im2 mask blur gauss * + * nonzero_mean im = no_out * + * so_meanmax im = result * + * so_calculate ref_meanmax im mask = result * + * simple_frame frame im_w im_h ov cs ms bf option = result * + * corner_frame frame im_w im_h ov cs ms bf = result * + * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * + * complex_frame frame im_w im_h ov cs es ms bf option= result * + * complex_edge ra rb t bl d = rc * + * frame_lr_min r_l r_r target bw = result * + * frame_tb_min r_t r_b target bw = result * + * frame_position_image im ref os colour= result * + * merge_array bw arr = result * + * merge_to_scale im target blend dir = result * + * select_ellipse line width = mask * + * select_tetragon p1 p2 p3 p4 = mask * + * select_polygon pt_list = mask * + * perspective_transform to from = trans'' * + * sort_pts_clockwise l = l'' * + */ + +/* Called from: +* _NG_Extra.def Clone_area_item +*/ +so_balance ref_meanmax im1 im2 mask gauss + = result + { + //ref_meanmax = so_meanmax im1; + so_values = so_calculate ref_meanmax im2 mask; + im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 + = im2'' + {im2'' = im2 * (so_values?0) + (so_values?1);} + // Option to convert replacement image to scaled gaussian noise + im2_cor = im2_cor_a, gauss == false + = clip2fmt im2_cor_a.format gauss_im + {gauss_im = + gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 +(deviation im2_cor_a);} + result = im_blend (get_image mask) (get_image +im2_cor) (get_image im1); + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the mean of the non zero pixels. + * + * Called from: + * _NG_utilities so_meanmax + */ +nonzero_mean im = no_out + { + zero_im = (im == 0); + zero_mean = mean zero_im; + no_mean = mean im; + no_out = no_mean/(1 - (zero_mean/255)); + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the max and nonzero mean of an image + * + * Called from: + * _NG_utilities so_balance + * _NG_utilities so_calculate + * _NG_Extra.def Clone_area_item + * _NG_Extra.def Balance_item.Balance_find_item + */ +so_meanmax im = result + { + mean_of_im = nonzero_mean im; + adjusted_im = im - mean_of_im; + max_of_im = max adjusted_im; + + result = [mean_of_im, max_of_im]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the scale and offset required to match a reference mean and max + * + * Called from: + * _NG_utilities so_balance + * _NG_Extra.def Balance_item.Balance_find_item + */ +so_calculate ref_meanmax im mask = result + { + im' = if mask then im else 0; + im_values = so_meanmax im'; + + mean_of_ref = ref_meanmax?0; + mean_of_im = im_values?0; + + max_of_ref = ref_meanmax?1; + max_of_im = im_values?1; + + scale = (max_of_ref)/(max_of_im); + offset = mean_of_ref - (mean_of_im * scale); + result = [ scale, offset ]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Extends or shortens the central sections of a simple frame to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Simple_frame_item + */ +simple_frame frame im_w im_h ov cs ms bf option = result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + ms'' = (1 - cs); + + //Regions + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl, option == true + = Region_relative frame cs' 0 cs cs; + r_bl = Region_relative frame 0 cs' cs cs; + r_br = fliplr r_bl, option == true + = Region_relative frame cs' cs' cs cs; + + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = Region_relative frame ms' ms'' ms cs; + r_ml = Region_relative frame 0 ms' cs ms; + r_mr = fliplr r_ml, option == true + = Region_relative frame ms'' ms' cs ms; + + result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Frame_corner_item + */ +corner_frame frame im_w im_h ov cs ms bf = result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + + //Regions + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl; + r_bl = fliptb r_tl; + r_br = fliplr r_bl; + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = fliptb r_mt; + r_ml = Region_relative frame 0 ms' cs ms;; + r_mr = fliplr r_ml; + result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Completes the frame building process for simple_frame and corner_frame. + * + * _NG_utilities simple_frame + * _NG_utilities corner_frame + */ +build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result + { + //Find pixel thickness of frames section + s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); + s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); + + w_target = im_w + (2 * (s_width - ov)); + h_target = im_h + (2 * (s_height - ov)); + + blend = bf * r_tl.width; + + cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) + = w_target; + ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) + = h_target; + + //Use regions to produce sections + top = merge_to_scale r_mt cw_target blend 0; + bottom = merge_to_scale r_mb cw_target blend 0; + left = merge_to_scale r_ml ch_target blend 1; + right = merge_to_scale r_mr ch_target blend 1; + middle = Image + (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); + + //Build sections into full frame. + row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_tl, top, r_tr]]; + row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[left, middle, right]]; + row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_bl, bottom, r_br]]; + + result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) + = merge_array blend [[row_1], [row_2], [row_3]]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Extends or shortens the central sections of a frame, preserving any central details on each + * edge, to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Complex_frame_item + */ +complex_frame frame im_w im_h ov cs es ms bf option= result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + es' = (0.25 - (es/2)); + + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl, option == true + = Region_relative frame cs' 0 cs cs; + r_bl = Region_relative frame 0 cs' cs cs; + r_br = fliplr r_bl, option == true + = Region_relative frame cs' cs' cs cs; + + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = Region_relative frame ms' cs' ms cs; + r_ml = Region_relative frame 0 ms' cs ms; + r_mr = fliplr r_ml, option == true + = Region_relative frame cs' ms' cs ms; + + r_et = Region_relative frame es' 0 es cs; + r_eb = Region_relative frame es' cs' es cs; + r_el = Region_relative frame 0 es' cs es; + r_er = fliplr r_el, option == true + = Region_relative frame cs' es' cs es; + + //Find pixel thickness of frames section + s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); + s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); + + w_target = im_w + (2 * (s_width - ov)); + h_target = im_h + (2 * (s_height - ov)); + min_size = foldr1 min_pair [r_tl.width, r_tl.height, + r_mt.width, r_mt.height, + r_et.width, r_et.height]; + blend = bf * min_size; + + cw_target = w_target - (2 * r_tl.width) + (2 * blend); + ch_target = h_target - (2 * r_tl.height) + (2 * blend); + + top = complex_edge r_mt r_et cw_target blend 0; + bottom = complex_edge r_mb r_eb cw_target blend 0; + left = complex_edge r_ml r_el ch_target blend 1; + right = complex_edge r_mr r_er ch_target blend 1; + middle = Image + (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); + + //Build regions into full frame. + row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_tl, top, r_tr]]; + row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[left, middle, right]]; + row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_bl, bottom, r_br]]; + result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) + = merge_array blend [[row_1], [row_2], [row_3]]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Function called by complex frame, used to produce section + * + * Called from: + * _NG_utilities.def complex_frame + */ +complex_edge ra rb t bl d = rc + { + e1 = ceil (ra.width - t)/2, d == 0 + = 0; + e2 = 0, d == 0 + = ceil (ra.height - t)/2; + e3 = t, d == 0 + = ra.width; + e4 = ra.height, d == 0 + = t; + + check = ra.width, d == 0; + = ra.height; + + rai = get_image ra; + + t2 = (t - ra.width + (2 * bl))/2, d == 0 + = (t - ra.height + (2 * bl))/2; + + rc = ra , t <= 0 + = Image (im_extract_area rai e1 e2 e3 e4), t <= check + = merge_array bl [[rb',ra,rb']], d == 0 + = merge_array bl [[rb'],[ra],[rb']] + {rb' = merge_to_scale rb t2 bl d;} + }; + +////////////////////////////////////////////////////////////////////////////// +/* Blends two images left/right to produce an image a specific width. + * + * _NG_utilities build_frame + * _NG_utilities complex_frame + */ +frame_lr_min r_l r_r target bw = result + { + //Calculating the new widh required for each image. + no = (target/2 + bw); + n_w = no, (r_l.width > no) + = r_l.width; + + //Removing excess from what will be the middle of the final image. + n_l = im_extract_area r_l.value 0 0 n_w r_l.height; + n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; + + //Merge the two image together with a bw*2 pixel overlap. + result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Blends two images top/bottom to produce an image a specific width. + * + * _NG_utilities build_frame + * _NG_utilities complex_frame + */ +frame_tb_min r_t r_b target bw = result + { + //Calculating the new height required for each image. + no = (target/2 + bw); + n_h = no, (r_t.height > no) + = r_t.height; + + //Removing excess from what will be the middle of the final image. + n_t = im_extract_area r_t.value 0 0 r_t.width n_h; + n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; + + //Merge the two image together with a 50 pixel overlap. + result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Resixe canvas of an image to accomodate a frame and possible mount + * + * Called from: + * _NG_Extra.def Frame_item.Frame_corner_item + * _NG_Extra.def Frame_item.Simple_frame_item + * _NG_Extra.def Frame_item.Complex_frame_item + */ +frame_position_image im ref os colour= result + { + background = image_new ref.width ref.height + im.bands im.format im.coding im.type colour 0 0; + + result = insert_noexpand xp yp im background + { + xp = (ref.width - im.width)/2; + yp = (ref.height - im.height - os)/2; + } + }; + +////////////////////////////////////////////////////////////////////////////// +/* Merges an array of images together according to blend width bw + * + * Called from: + * _NG_Utilites.def build_frame + * _NG_Utilites.def complex_frame + * _NG_Utilites.def complex_edge + */ +merge_array bw arr = result + { + merge_lr bw im1 im2 = im3 + { + bw' = get_header "Xsize" (get_image im1); + bw'' = -(bw' - bw); + im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; + } + merge_tb bw im1 im2 = im3 + { + bw' = get_header "Ysize" (get_image im1); + bw'' = -(bw' - bw); + im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; + } + + im_out = (image_set_origin 0 0 @ + foldl1 (merge_tb bw) @ + map (foldl1 (merge_lr bw))) arr; + result = Image im_out; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target + * + * Called from: + * _NG_Utilites.def build_frame + * _NG_Utilites.def complex_edge + */ +merge_to_scale im target blend dir = result + { + blend' = floor blend; + + //allow fir lr or tb process + var_a = im.width, dir == 0 + = im.height; + + var_w = im.width, dir == 1 + = target, target > blend' + = blend'; + var_h = im.height, dir == 0 + = target, target > blend' + = blend'; + + //total numner of copies of im requires, taking overlap into account. + no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); + + process im no = result + { + pr_a = get_header "Xsize" (get_image im), dir == 0 + = get_header "Ysize" (get_image im); + pr_b = -(pr_a - blend' + 1); + + im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 + = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; + no' = no - 1; + + result = im', no' < 1 + = process im' no'; + } + + im_tmp = im.value, var_a > target + = process im no_loops; + + result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects an elispe based on a line and a width + * + * Called from: + * _NG_Extra.def Select_item.Elipse + */ +select_ellipse line width = mask + { + im = Image (get_image line); + + //Make a 2 band image whose value equals its coordinates. + im_coor = Image (make_xy im.width im.height); + + //Adjust the values to center tham on (line.left, line.top) + im_cent = im_coor - Vector [line.left,line.top]; + + w = line.width; + h = line.height; + + angle = 270, w == 0 && h < 0 + = 90, w == 0 && h >= 0 + = 360 + atan (h/w), w > 0 && h < 0 + = atan (h/w), w > 0 && h >= 0 + = 180 + atan (h/w); + + a = ( (h ** 2) + (w ** 2) )**0.5; + b = a * width; + + x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); + y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); + + mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Select_item.Tetragon + * _NG_Extra.def Perspective_item + */ +select_tetragon p1 p2 p3 p4 = mask + { + //Put points in clockwise order starting at the top left. + pt_list = sort_pts_clockwise [p1, p2, p3, p4]; + + pair_list = [ + [ pt_list?0, pt_list?1 ], + [ pt_list?1, pt_list?2 ], + [ pt_list?2, pt_list?3 ], + [ pt_list?3, pt_list?0 ] ]; + + //Make xy image the same size as p1.image; + im_xy = Image (make_xy p1.image.width p1.image.height); + white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); + + mask = foldl process white pair_list; + + /* Treat each pair of point as a vector going from p1 to p2, + * then select all to right of line. This is done for each pair, + * the results are all combined to select the area defined by + * the four points. + */ + process im_in pair = im_out + { + x = (pair?0).left; + y = (pair?0).top; + x'= (pair?1).left; + y'= (pair?1).top; + + w = x' - x; + h = y' - y; + + m = 0, x == x' + = (y-y')/(x-x'); + c = 0, x == x' + = ((y*x') - (y'*x))/(x' - x); + + mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 + = im_xy?1 - (im_xy?0 * m) <= c, w < 0 + = im_xy?0 <= x, w == 0 && h > 0 + = im_xy?0 >= x; + + im_out = im_in & mask; + } + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Select_item.Polygon + */ +select_polygon pt_list = mask + { + group_check = is_Group pt_list; + pt_l = pt_list.value, group_check + = pt_list; + + im = Image (get_image (pt_l?0)); + im_xy = Image (make_xy im.width im.height); + black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); + + x = im_xy?0; + y = im_xy?1; + + pt_l' = grp_trip pt_l; + + mask = foldl process black pt_l'; + + /*Takes a group adds the first two the end and then creates a lists of + *lists [[a, b, c], [b, c, d] .... [x, a, b]] + */ + grp_trip l = l'' + { + px = take 2 l; + l' = join l px; + start = [(take 3 l')]; + rest = drop 3 l'; + + process a b = c + { + x = (last a)?1; + x'= (last a)?2; + x'' = [[x, x', b]]; + c = join a x''; + } + + l'' = foldl process start rest; + }; + + process im_in triplet = im_out + { + p1 = triplet?0; + p2 = triplet?1; + p3 = triplet?2; + + //check for change in x direction between p1-p2 and p2 -p3 + dir_1 = sign (p2.left - p1.left); + dir_2 = sign (p3.left - p2.left); + dir = dir_1 + dir_2; + + //define min x limit. + min_x = p1.left, p1.left < p2.left + = p2.left + 1, dir != 0 + = p2.left; + + //define max x limit. + max_x = p1.left, p1.left > p2.left + = p2.left - 1, dir != 0 + = p2.left; + + //equation of line defined by p1 and p2 + m = line_m p1 p2; + c = line_c p1 p2; + + //Every thing below the line + im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); + + im_out = im_in ^ im_test; + } + + line_c p1 p2 = c + {m = line_m p1 p2; + c = p1.top - (m * p1.left);}; + + line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left + = 0; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Perspective_match_item + * _NG_Extra.def Perspective_item + */ +perspective_transform to from = trans'' + { + /* + * Tramsformation matrix is calculated on the bases of the following functions: + * x' = c0x + c1y + c2xy + c3 + * y' = c4x + c5y + c6xy + c7 + * + * The functions used in vips im_transform works based on the functions: + * x = x' + b0 + b2x' + b4y' + b6x'y' + * y = y' + b1 + b3x' + b5y' + b7x'y' + * + * and is applied in the form of the matrix: + * + * [[b0, b1], + * [b2, b3], + * [b4, b5], + * [b6, b7]] + * + * Therefore our required calculated matrix will be + * + * [[ c3 , c7], + * [(c0 - 1) , c4], + * [ c1 , (c5 - 1)], + * [ c2 , c6]] + * + * to = [x1, y1, x2, y2, x3, y3, x4, y4] + * from = [x1', y1', x2', y2', x3', y3', x4', y4'] + * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] + * + */ + + to' = Matrix + [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], + [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], + [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], + [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; + + from' = Matrix (transpose [from]); + + to'' = to' ** (-1); + + trans = to'' * from'; + trans' = trans.value; + trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], + [((trans'?0)?0 - 1), (trans'?4)?0 ], + [(trans'?1)?0, ((trans'?5)?0 - 1)], + [(trans'?2)?0, (trans'?6)?0 ]]; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Sort a list of points into clockwise order. + * + * Called from: + * _NG_utilities.def select_tetragon + * _NG_Extra.def Perspective_match_item + * _NG_Extra.def Perspective_item + */ +sort_pts_clockwise l = l'' + { + // sort functions: + f_top a b = a.top < b.top; + f_left a b = a.left < b.left; + f_right a b = a.left > b.left; + + l' = sortc f_top l; + l'_a = take 2 l'; + l'_b = drop 2 l'; + + l''_a = sortc f_left l'_a; + l''_b = sortc f_right l'_b; + l'' = join l''_a l''_b; + }; + +Mount_options _ctype _ppcm = class + { + _vislevel = 3; + apply = Toggle "Apply mount options" false; + ls = Expression "Lower mount section bigger by (cm)" 0; + mount_colour = Colour _ctype [0, 0, 0]; + _los = ls.expr * _ppcm; + }; + +Frame_variables comp = class + { + _vislevel = 3; + + scale_factor = Expression "scale the size of the frame by" 1; + + /* These sliders define the fraction of the frames width or height is extracted + * to produce each of the particular regions. + */ + corner_section = Scale "Corner section" 0.1 1 0.5; + edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 + = "Only required for complex frames"; + middle_section = Scale "Middle section" 0.1 1 0.2; + blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; + option = Toggle "Use mirror of left-side to make right" true; + }; + diff --git a/share/nip2/compat/8.5/_list.def b/share/nip2/compat/8.5/_list.def new file mode 100644 index 00000000..d2ef4a1f --- /dev/null +++ b/share/nip2/compat/8.5/_list.def @@ -0,0 +1,482 @@ +/* any l: or all the elements of list l together + * + * any (map (equal 0) list) == true, if any element of list is zero. + * any :: [bool] -> bool + */ +any = foldr logical_or false; + +/* all l: and all the elements of list l together + * + * all (map (==0) list) == true, if every element of list is zero. + * all :: [bool] -> bool + */ +all = foldr logical_and true; + +/* concat l: join a list of lists together + * + * concat ["abc","def"] == "abcdef". + * concat :: [[*]] -> [*] + */ +concat l = foldr join [] l; + +/* delete eq x l: delete the first x from l + * + * delete equal 'b' "abcdb" == "acdb" + * delete :: (* -> bool) -> * -> [*] -> [*] + */ +delete eq a l + = [], l == [] + = y, eq a b + = b : delete eq a y +{ + b:y = l; +} + +/* difference eq a b: delete b from a + * + * difference equal "asdf" "ad" == "sf" + * difference :: (* -> bool) -> [*] -> [*] -> [*] + */ +difference = foldl @ converse @ delete; + +/* drop n l: drop the first n elements from list l + * + * drop 3 "abcd" == "d" + * drop :: num -> [*] -> [*] + */ +drop n l + = l, n <= 0 || l == [] + = drop (n - 1) (tl l); + +/* dropwhile fn l: drop while fn is true + * + * dropwhile is_digit "1234pigs" == "pigs" + * dropwhile :: (* -> bool) -> [*] -> [*] + */ +dropwhile fn l + = [], l == [] + = dropwhile fn x, fn a + = l +{ + a:x = l; +} + +/* extract n l: extract element at index n from list l + */ +extract = converse subscript; + +/* filter fn l: return all elements of l for which predicate fn holds + * + * filter is_digit "1one2two3three" = "123" + * filter :: (* -> bool) -> [*] -> [*] + */ +filter fn l + = foldr addif [] l +{ + addif x l + = x : l, fn x; + = l; +} + +/* flatten x: flatten a list of lists of things into a simple list + * + * flatten :: [[*]] -> [*] + */ +flatten x + = foldr flat [] x, is_list x + = x +{ + flat x sofar + = foldr flat sofar x, is_list x + = x : sofar; +} + +/* foldl fn st l: fold list l from the left with function fn and start st + * + * Start from the left hand end of the list (unlike foldr, see below). + * foldl is less useful (and much slower). + * + * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) + * foldl :: (* -> ** -> *) -> * -> [**] -> * + */ +foldl fn st l + = st, l == [] + = foldl fn (fn st x) xs +{ + x:xs = l; +} + +/* foldl1 fn l: like foldl, but use the 1st element as the start value + * + * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) + * foldl1 :: (* -> * -> *) -> [*] -> * + */ +foldl1 fn l + = [], l == [] + = foldl fn x xs +{ + x:xs = l; +} + +/* foldr fn st l: fold list l from the right with function fn and start st + * + * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) + * foldr :: (* -> ** -> **) -> ** -> [*] -> ** + */ +foldr fn st l + = st, l == [] + = fn x (foldr fn st xs) +{ + x:xs = l; +} + +/* foldr1 fn l: like foldr, but use the last element as the start value + * + * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) + * foldr1 :: (* -> * -> *) -> [*] -> * + */ +foldr1 fn l + = [], l == [] + = x, xs == [] + = fn x (foldr1 fn xs) +{ + x:xs = l; +} + +/* Search a list for an element, returning its index (or -1) + * + * index (equal 12) [13,12,11] == 1 + * index :: (* -> bool) -> [*] -> real + */ +index fn list + = search list 0 +{ + search l n + = -1, l == [] + = n, fn x + = search xs (n + 1) + { + x:xs = l; + } +} + +/* init l: remove last element of list l + * + * The dual of tl. + * init [1,2,3] == [1,2] + * init :: [*] -> [*] + */ +init l + = error "init of []", l == []; + = [], tl l == []; + = x : init xs +{ + x:xs = l; +} + +/* iterate f x: repeatedly apply f to x + * + * return the infinite list [x, f x, f (f x), ..]. + * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] + * iterate :: (* -> *) -> * -> [*] + */ +iterate f x = x : iterate f (f x); + +/* join_sep sep l: join a list with a separator + * + * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" + * join_sep :: [*] -> [[*]] -> [*] + */ +join_sep sep l + = foldl1 fn l +{ + fn a b = a ++ sep ++ b; +} + +/* last l: return the last element of list l + * + * The dual of hd. last [1,2,3] == 3 + * last :: [*] -> [*] + */ +last l + = error "last of []", l == [] + = x, xs == [] + = last xs +{ + x:xs = l; +} + +/* len l: length of list l + * (see also is_list_len and friends in predicate.def) + * + * len :: [*] -> num + */ +len l + = 0, l == [] + = 1 + len (tl l); + +/* limit l: return the first element of l which is equal to its predecessor + * + * useful for checking for convergence + * limit :: [*] -> * + */ +limit l + = error "incorrect use of limit", + l == [] || tl l == [] || tl (tl l) == [] + = a, a == b + = limit (b : x) +{ + a:b:x = l; +} + +/* Turn a function of n args into a function which takes a single arg of an + * n-element list. + */ +list_1ary fn x = fn x?0; +list_2ary fn x = fn x?0 x?1; +list_3ary fn x = fn x?0 x?1 x?2; +list_4ary fn x = fn x?0 x?1 x?2 x?3; +list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; +list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; +list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; + +/* map fn l: map function fn over list l + * + * map :: (* -> **) -> [*] -> [**] + */ +map f l + = [], l == []; + = f (hd l) : map f (tl l); + +/* map2 fn l1 l2: map two lists together with fn + * + * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] + */ +map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); + +/* map3 fn l1 l2 l3: map three lists together with fn + * + * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] + */ +map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); + +/* member l x: true if x is a member of list l + * + * is_digit == member "0123456789" + * member :: [*] -> * -> bool + */ +member l x = any (map (equal x) l); + +/* merge b l r: merge two lists based on a bool list + * + * merge :: [bool] -> [*] -> [*] -> [*] + */ +merge p l r + = [], p == [] || l == [] || r == [] + = a : merge z x y, c + = b : merge z x y +{ + a:x = l; + b:y = r; + c:z = p; +} + +/* mkset eq l: remove duplicates from list l using equality function + * + * mkset :: (* -> bool) -> [*] -> [*] + */ +mkset eq l + = [], l == [] + = a : filter (not @ eq a) (mkset eq x) +{ + a:x = l; +} + +/* postfix l r: add r to the end of list l + * + * The dual of ':'. + * postfix :: [*] -> ** -> [*,**] + */ +postfix l r = l ++ [r]; + +/* repeat x: make an infinite list of xes + * + * repeat :: * -> [*] + */ +repeat x = map (const x) [1..]; + +/* replicate n x: make n copies of x in a list + * + * replicate :: num -> * -> [*] + */ +replicate n x = take n (repeat x); + +/* reverse l: reverse list l + * + * reverse :: [*] -> [*] + */ +reverse l = foldl (converse cons) [] l; + +/* scanl fn st l: apply (foldl fn r) to every initial segment of a list + * + * scanl add 0 [1,2,3] == [1,3,6] + * scanl :: (* -> ** -> *) -> * -> [**] -> [*] + */ +scanl fn st l + = st, l == [] + = st' : scanl fn st' xs +{ + x:xs = l; + st' = fn st x; +} + +/* sort l: sort list l into ascending order + * + * sort :: [*] -> [*] + */ +sort l = sortc less_equal l; + +/* sortc comp l: sort list l into order using a comparision function + * + * Uses merge sort (n log n behaviour) + * sortc :: (* -> * -> bool) -> [*] -> [*] + */ +sortc comp l + = l, n <= 1 + = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) +{ + n = len l; + n2 = (int) (n / 2); + + /* merge l1 l2: merge sorted lists l1 and l2 to make a single + * sorted list + */ + merge l1 l2 + = l2, l1 == [] + = l1, l2 == [] + = a : merge x (b : y), comp a b + = b : merge (a : x) y + { + a:x = l1; + b:y = l2; + } +} + +/* sortpl pl l: sort by a list of predicates + * + * sortpl :: (* -> bool) -> [*] -> [*] + */ +sortpl pl l + = sortc (test pl) l +{ + /* Comparision function ... put true before false, if equal move on to + * the next predicate. + */ + test pl a b + = true, pl == [] + = ta, ta != tb + = test (tl pl) a b + { + ta = pl?0 a; + tb = pl?0 b; + } +} + +/* sortr l: sort list l into descending order + * + * sortr :: [*] -> [*] + */ +sortr l = sortc more l; + +/* split fn l: break a list into sections separated by many fn + * + * split is_space " hello world " == ["hello", "world"] + * split is_space " " == [] + * split :: (* -> bool) -> [*] -> [[*]] + */ +split fn l + = [], l == [] || l' == [] + = head : split fn tail +{ + nfn = not @ fn; + + l' = dropwhile fn l; + head = takewhile nfn l'; + tail = dropwhile nfn l'; +} + +/* splits fn l: break a list into sections separated by a single fn + * + * split (equal ',') ",,1" == ["", "", "1"] + * split :: (* -> bool) -> [*] -> [[*]] + */ +splits fn l + = [], l == [] + = head : splits fn tail +{ + fn' = not @ fn; + dropif x + = [], x == [] + = tl x; + + head = takewhile fn' l; + tail = dropif (dropwhile fn' l); +} + +/* splitpl fnl l: split a list up with a list of predicates + * + * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] + * splitpl :: [* -> bool] -> [*] -> [[*]] + */ +splitpl fnl l + = l, fnl == [] + = head : splitpl (tl fnl) tail +{ + head = takewhile (hd fnl) l; + tail = dropwhile (hd fnl) l; +} + +/* split_lines n l: split a list into equal length lines + * + * split_lines 4 "1234567" == ["1234", "567"] + * splitl :: int -> [*] -> [[*]] + */ +split_lines n l + = [], l == [] + = take n l : split_lines n (drop n l); + +/* take n l: take the first n elements from list l + * take :: num -> [*] -> [*] + */ +take n l + = [], n <= 0 + = [], l == [] + = hd l : take (n-1) (tl l); + +/* takewhile fn l: take from the front of a list while predicate fn holds + * + * takewhile is_digit "123onetwothree" == "123" + * takewhile :: (* -> bool) -> [*] -> [*] + */ +takewhile fn l + = [], l == [] + = hd l : takewhile fn (tl l), fn (hd l) + = []; + +/* zip2 l1 l2: zip two lists together + * + * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] + * zip2 :: [*] -> [**] -> [[*,**]] + */ +zip2 l1 l2 + = [], l1 == [] || l2 == [] + = [hd l1, hd l2] : zip2 (tl l1) (tl l2); + +/* zip3 l1 l2 l3: zip three lists together + * + * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] + * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] + */ +zip3 l1 l2 l3 + = [], l1 == [] || l2 == [] || l3 == [] + = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); diff --git a/share/nip2/compat/8.5/_magick.def b/share/nip2/compat/8.5/_magick.def new file mode 100644 index 00000000..01c22b18 --- /dev/null +++ b/share/nip2/compat/8.5/_magick.def @@ -0,0 +1,1107 @@ +/* + + ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). + + 1-Apr-2014 + Minor corrections to Geometry_widget and Alpha. + Added loads of widgets and Menuactions. + Not fully tested. + 5-Apr-2014 + Many more menu actions. + Reorganised Magick menu. + 10-Apr-2014 + Many more menu actions. + 11-Apr-2014 jcupitt + Split to separate _magick.def + Add 0-ary and 2-ary system + Put utility funcs into a Magick class + 11-Apr-2014 snibgo + Added VirtualPixelBack for cases where background is only relevant when VP=Background + 17-Apr-2014 snibgo + Many small changes. + 2-May-2014 jcupitt + Added Magick.version + 30-June-2014 + Put single-quotes around command exe to help win + 1-July-2014 + Automatically fall back to gm if we can't find convert + 17-July-2014 + better GM support + + + Last update: 17-July-2014. + + For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. + +*/ + +/* Put these in a class to avoid filling the main namespace with IM stuff. + */ + +Magick = class { + + // first gm on path, or "" + gm_path = search_for "gm"; + + // first convert on $PATH, or "" + // we check for the convert we ship first + convert_path + = vips_convert, vips_convert != "" + = search_for "convert" + { + // the convert we ship with the vips binary on some platforms, or "" + vips_convert + = search (path_absolute convert) + { + vipshome = path_parse (expand "$VIPSHOME"); + convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; + } + } + + use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; + + // Are we in GM or IM mode? + use_gm + = true, use_gm_pref && gm_path != "" + = false, !use_gm_pref && convert_path != "" + = false, convert_path != "" + = true, gm_path != "" + = error "neither IM nor GM executable found"; + + command_path + = gm_path, use_gm + = convert_path; + + // try to get the version as eg. [6, 7, 7, 10] + // GM versions are smaller, typically [1, 3, 18] + version + = map parse_int (split (member ".-") version_string) + { + [output] = vips_call "system" + ["'" ++ command_path ++ "' -version"] [$log=>true]; + version_string + = (split (equal ' ') output)?1, use_gm + = (split (equal ' ') output)?2; + } + + // make a command-line ... args is a [str] we join with spaces + command args + = "'" ++ command_path ++ "' " ++ join_sep " " args' + { + args' + = ["convert"] ++ args, use_gm + = args; + } + + // capabilities ... different versions support different features, we + // turn features on and off based on these + + // would probably be better to test for caps somehow + has_intensity + = false, use_gm + = version?0 > 6 || version?1 > 7; + has_channel + = false, use_gm + = version?0 > 6 || version?1 > 7; + + system0 cmd = system_image0 cmd; + system cmd x = map_unary (system_image cmd) x; + system2 cmd x y = map_binary (system_image2 cmd) x y; + system3 cmd x y z = map_trinary (system_image3 cmd) x y z; + + radius_widget = Scale "Radius" 0 100 10; + sigma_widget = Scale "Sigma" 0.1 10 1; + angle_widget = Scale "Angle (degrees)" (-360) 360 0; + text_widget = String "Text to draw" "AaBbCcDdEe"; + + gamma_widget = Scale "Gamma" 0 10 1; + colors_widget = Scale "Colors" 1 10 3; + resize_widget = Scale "Resize (percent)" 0 500 100; + fuzz_widget = Scale "Fuzz (percent)" 0 100 0; + blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; + + // a colour with no enclosing quotes ... use this if we know there are + // some quotes at an outer level + print_colour_nq triple + = concat ["#", concat (map fmt triple)] + { + fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); + } + + // we need the quotes because # is the comment character in *nix + print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; + + Foreground triple = class + Colour "sRGB" triple { + + _flag = "-fill " ++ print_colour triple; + + Colour_edit space triple = this.Foreground triple; + } + foreground_widget = Foreground [0, 0, 0]; + + GeneralCol triple = class + Colour "sRGB" triple { + + _flag = print_colour_nq triple; + + Colour_edit space triple = this.GeneralCol triple; + } + generalcol_widget = GeneralCol [0, 0, 0]; + + Background triple = class + Colour "sRGB" triple { + + isNone = Toggle "None (transparent black)" false; + + _flag = "-background " ++ if isNone then "None" else print_colour triple; + + Colour_edit space triple = this.Background triple; + } + background_widget = Background [255, 255, 255]; + + Bordercol triple = class + Colour "sRGB" triple { + + _flag = "-bordercolor " ++ print_colour triple; + + Colour_edit space triple = this.Bordercol triple; + } + bordercol_widget = Bordercol [0, 0, 0]; + + Mattecol triple = class + Colour "sRGB" triple { + + _flag = "-mattecolor " ++ print_colour triple; + + Colour_edit space triple = this.Mattecol triple; + } + mattecol_widget = Mattecol [189, 189, 189]; + + // FIXME: Undercolour, like many others, can have alpha channel. + // How does user input this? With a slider? + Undercol triple = class + Colour "sRGB" triple { + + isNone = Toggle "None (transparent black)" true; + + _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); + + Colour_edit space triple = this.Undercol triple; + } + undercol_widget = Undercol [0, 0, 0]; + + changeCol_widget = class { + _vislevel = 3; + + colour = GeneralCol [0, 0, 0]; + fuzz = fuzz_widget; + nonMatch = Toggle "change non-matching colours" false; + } + + Alpha alpha = class + Option_string "Alpha" [ + "On", + "Off", + "Set", + "Opaque", + "Transparent", + "Extract", + "Copy", + "Shape", + "Remove", + "Background" + ] alpha { + + _flag = "-alpha " ++ alpha; + + Option_edit caption labels value = this.Alpha labels?value; + } + alpha_widget = Alpha "On"; + + Antialias value = class + Toggle "Antialias" value { + + _flag + = "-antialias", value + = "+antialias"; + + Toggle_edit caption value = this.Antialias value; + } + antialias_widget = Antialias true; + + Builtin builtin = class + Option_string "Builtin" [ + // See http://www.imagemagick.org/script/formats.php + "rose:", + "logo:", + "wizard:", + "granite:", + "netscape:" + ] builtin { + + _flag = builtin; + + Option_edit caption labels value = this.Builtin labels?value; + } + builtin_widget = Builtin "rose:"; + + + channels_widget = class { + // FIXME? Can we grey-out alpha when we have no alpha channel, + // show CMY(K) instead of RGB(K) etc? + // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. + ChanR valueR = class + Toggle "Red" valueR { + + _flag + = "R", valueR + = ""; + + Toggle_edit caption valueR = this.ChanR valueR; + } + channelR = ChanR true; + + ChanG valueG = class + Toggle "Green" valueG { + + _flag + = "G", valueG + = ""; + + Toggle_edit caption valueG = this.ChanG valueG; + } + channelG = ChanG true; + + ChanB valueB = class + Toggle "Blue" valueB { + + _flag + = "B", valueB + = ""; + + Toggle_edit caption valueB = this.ChanB valueB; + } + channelB = ChanB true; + + ChanK valueK = class + Toggle "Black" valueK { + + _flag + = "K", valueK + = ""; + + Toggle_edit caption valueK = this.ChanK valueK; + } + channelK = ChanK true; + + ChanA valueA = class + Toggle "Alpha" valueA { + + _flag + = "A", valueA + = ""; + + Toggle_edit caption valueA = this.ChanA valueA; + } + channelA = ChanA false; + + ChanSy valueSy = class + Toggle "Sync" valueSy { + + _flag + = ",sync", valueSy + = ""; + + Toggle_edit caption valueSy = this.ChanSy valueSy; + } + channelSy = ChanSy true; + + _rgbka = concat [channelR._flag, + channelG._flag, + channelB._flag, + channelK._flag, + channelA._flag + ]; + + _flag + = "", _rgbka == "" || !has_channel + = concat [ "-channel ", + _rgbka, + channelSy._flag + ]; + } + + ch_widget = channels_widget; + + Colorspace colsp = class + Option_string "Colorspace" [ + "CIELab", + "CMY", + "CMYK", + "Gray", + "HCL", + "HCLp", + "HSB", + "HSI", + "HSL", + "HSV", + "HWB", + "Lab", + "LCH", + "LCHab", + "LCHuv", + "LMS", + "Log", + "Luv", + "OHTA", + "Rec601Luma", + "Rec601YCbCr", + "Rec709Luma", + "Rec709YCbCr", + "RGB", + "scRGB", + "sRGB", + "Transparent", + "XYZ", + "YCbCr", + "YDbDr", + "YCC", + "YIQ", + "YPbPr", + "YUV" + ] colsp { + + _flag = colsp; + + Option_edit caption labels value = this.Colorspace labels?value; + } + colorspace_widget = Colorspace "sRGB"; + + Compose comp = class + Option_string "Compose method" [ + "Atop", + "Blend", + "Blur", + "Bumpmap", + "ChangeMask", + "Clear", + "ColorBurn", + "ColorDodge", + "Colorize", + "CopyBlack", + "CopyBlue", + "CopyCyan", + "CopyGreen", + "Copy", + "CopyMagenta", + "CopyOpacity", + "CopyRed", + "CopyYellow", + "Darken", + "DarkenIntensity", + "DivideDst", + "DivideSrc", + "Dst", + "Difference", + "Displace", + "Dissolve", + "Distort", + "DstAtop", + "DstIn", + "DstOut", + "DstOver", + "Exclusion", + "HardLight", + "Hue", + "In", + "Lighten", + "LightenIntensity", + "LinearBurn", + "LinearDodge", + "LinearLight", + "Luminize", + "Mathematics", + "MinusDst", + "MinusSrc", + "Modulate", + "ModulusAdd", + "ModulusSubtract", + "Multiply", + "None", + "Out", + "Overlay", + "Over", + "PegtopLight", + "PinLight", + "Plus", + "Replace", + "Saturate", + "Screen", + "SoftLight", + "Src", + "SrcAtop", + "SrcIn", + "SrcOut", + "SrcOver", + "VividLight", + "Xor" + ] comp { + + _flag = "-compose " ++ comp; + + Option_edit caption labels value = this.Compose labels?value; + } + compose_widget = Compose "Over"; + // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. + + // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack + + coordinate_widget = class { + _vislevel = 3; + + x = Expression "X" 0; + y = Expression "Y" 0; + + _flag = concat [print x.expr, ",", print y.expr]; + }; + + Distort distort = class + Option_string "Distort" [ + "Affine", + "AffineProjection", + "ScaleRotateTranslate", + "SRT", + "Perspective", + "PerspectiveProjection", + "BilinearForward", + "BilinearReverse", + "Polynomial", + "Arc", + "Polar", + "DePolar", + "Barrel", + "BarrelInverse", + "Shepards", + "Resize" + ] distort { + + _flag = distort; + + Option_edit caption labels value = this.Distort labels?value; + } + distort_widget = Distort "SRT"; + + Dither dither = class + Option_string "Dither" [ + "None", + "FloydSteinberg", + "Riemersma" + ] dither { + + _flag = "-dither " ++ dither; + + Option_edit caption labels value = this.Dither labels?value; + } + dither_widget = Dither "FloydSteinberg"; + + Evaluate eval = class + Option_string "Evaluate operation" [ + "Abs", + "Add", + "AddModulus", + "And", + "Cos", + "Cosine", + "Divide", + "Exp", + "Exponential", + "GaussianNoise", + "ImpulseNoise", + "LaplacianNoise", + "LeftShift", + "Log", + "Max", + "Mean", + "Median", + "Min", + "MultiplicativeNoise", + "Multiply", + "Or", + "PoissonNoise", + "Pow", + "RightShift", + "Set", + "Sin", + "Sine", + "Subtract", + "Sum", + "Threshold", + "ThresholdBlack", + "ThresholdWhite", + "UniformNoise", + "Xor" + ] eval { + + _flag = "-evaluate " ++ eval; + + Option_edit caption labels value = this.Evaluate labels?value; + } + evaluate_widget = Evaluate "Add"; + + Filter filt = class + Option_string "Filter" [ + "default", + "Bartlett", + "Blackman", + "Bohman", + "Box", + "Catrom", + "Cosine", + "Cubic", + "Gaussian", + "Hamming", + "Hann", + "Hermite", + "Jinc", + "Kaiser", + "Lagrange", + "Lanczos", + "Lanczos2", + "Lanczos2Sharp", + "LanczosRadius", + "LanczosSharp", + "Mitchell", + "Parzen", + "Point", + "Quadratic", + "Robidoux", + "RobidouxSharp", + "Sinc", + "SincFast", + "Spline", + "Triangle", + "Welch" + ] filt { + + _flag = if filt == "default" then "" else "-filter " ++ filt; + + Option_edit caption labels value = this.Filter labels?value; + } + filter_widget = Filter "default"; + + Function func = class + Option_string "Function" [ + "Polynomial", + "Sinusoid", + "Arcsin", + "Arctan" + ] func { + + _flag = func; + + Option_edit caption labels value = this.Function labels?value; + } + function_widget = Function "Polynomial"; + +// "Polynomial (a[n], a[n-1], ... a[1], a[0])", +// "Sinusoid (freq, phase, amp, bias)", +// "Arcsin (width, centre, range, bias)", +// "Arctan (slope, centre, range, bias)" + + Gravity gravity = class + Option_string "Gravity" [ + "None", + "Center", + "East", + "Forget", + "NorthEast", + "North", + "NorthWest", + "SouthEast", + "South", + "SouthWest", + "West", + "Static" + ] gravity { + + _flag = "-gravity " ++ gravity; + + Option_edit caption labels value = this.Gravity labels?value; + } + gravity_widget = Gravity "Center"; + + ImageType imagetype = class + Option_string "Image type" [ + "Bilevel", + "ColorSeparation", + "ColorSeparationAlpha", + "ColorSeparationMatte", + "Grayscale", + "GrayscaleAlpha", + "GrayscaleMatte", + "Optimize", + "Palette", + "PaletteBilevelAlpha", + "PaletteBilevelMatte", + "PaletteAlpha", + "PaletteMatte", + "TrueColorAlpha", + "TrueColorMatte", + "TrueColor" + ] imagetype { + + _flag = "-type " ++ imagetype; + + Option_edit caption labels value = this.ImageType labels?value; + } + imagetype_widget = ImageType "TrueColor"; + + Intensity intensity = class + Option_string "Intensity (gray conversion)" [ + "Average", + "Brightness", + "Lightness", + "MS", + "Rec601Luma", + "Rec601Luminance", + "Rec709Luma", + "Rec709Luminance", + "RMS" + ] intensity { + + _flag + = "-intensity " ++ intensity, has_intensity + = ""; + + Option_edit caption labels value = this.Intensity labels?value; + } + intensity_widget = Intensity "Rec709Luminance"; + + Interpolate interp = class + Option_string "Interpolate" [ + "default", + "Average", + "Average4", + "Average9", + "Average16", + "Background", + "Bilinear", + "Blend", + "Integer", + "Mesh", + "Nearest", + "NearestNeighbor", + "Spline" + ] interp { + + _flag = if interp == "default" then "" else "-interpolate " ++ interp; + + Option_edit caption labels value = this.Interpolate labels?value; + } + interpolate_widget = Interpolate "default"; + + Kernel kernel = class + Option_string "Kernel" [ + "Unity", + "Gaussian", + "DoG", + "LoG", + "Blur", + "Comet", + "Binomial", + "Laplacian", + "Sobel", + "FreiChen", + "Roberts", + "Prewitt", + "Compass", + "Kirsch", + "Diamond", + "Square", + "Rectangle", + "Disk", + "Octagon", + "Plus", + "Cross", + "Ring", + "Peaks", + "Edges", + "Corners", + "Diagonals", + "LineEnds", + "LineJunctions", + "Ridges", + "ConvexHull", + "ThinSe", + "Skeleton", + "Chebyshev", + "Manhattan", + "Octagonal", + "Euclidean" + // FIXME: custom kernel + ] kernel { + + _flag = kernel; + + Option_edit caption labels value = this.Kernel labels?value; + } + kernel_widget = Kernel "Unity"; + + ModColSp msp = class + Option_string "modulate colorspace" [ + "HCL", + "HCLp", + "HSB", + "HSI", + "HSL", + "HSV", + "HWB", + "LCH" + ] msp { + + _flag = "-set option:modulate:colorspace " ++ msp; + + Option_edit caption labels value = this.ModColSp labels?value; + } + ModColSp_widget = ModColSp "HSL"; + + MorphMeth morph = class + Option_string "Method" [ + "Correlate", + "Convolve", + "Dilate", + "Erode", + "Close", + "Open", + "DilateIntensity", + "ErodeIntensity", + "CloseIntensity", + "OpenIntensity", + "Smooth", + "EdgeOut", + "EdgeIn", + "Edge", + "TopHat", + "BottomHat", + "HitAndMiss", + "Thinning", + "Thicken", + "Distance", + "IterativeDistance" + ] morph { + + _flag = morph; + + Option_edit caption labels value = this.MorphMeth labels?value; + } + morphmeth_widget = MorphMeth "Dilate"; + + Noise noise = class + Option_string "Noise" [ + "Gaussian", + "Impulse", + "Laplacian", + "Multiplicative", + "Poisson", + "Random", + "Uniform" + ] noise { + + _flag = "+noise " ++ noise; + + Option_edit caption labels value = this.Noise labels?value; + } + noise_widget = Noise "Gaussian"; + + Pattern pattern = class + Option_string "Noise" [ + // See http://www.imagemagick.org/script/formats.php + "bricks", + "checkerboard", + "circles", + "crosshatch", + "crosshatch30", + "crosshatch45", + "gray0", + "gray5", + "gray10", + "gray15", + "gray20", + "gray25", + "gray30", + "gray35", + "gray40", + "gray45", + "gray50", + "gray55", + "gray60", + "gray65", + "gray70", + "gray75", + "gray80", + "gray85", + "gray90", + "gray95", + "gray100", + "hexagons", + "horizontal", + "horizontal2", + "horizontal3", + "horizontalsaw", + "hs_bdiagonal", + "hs_cross", + "hs_diagcross", + "hs_fdiagonal", + "hs_horizontal", + "hs_vertical", + "left30", + "left45", + "leftshingle", + "octagons", + "right30", + "right45", + "rightshingle", + "smallfishscales", + "vertical", + "vertical2", + "vertical3", + "verticalbricks", + "verticalleftshingle", + "verticalrightshingle", + "verticalsaw" + ] pattern { + + _flag = "pattern:" ++ pattern; + + Option_edit caption labels value = this.Pattern labels?value; + } + pattern_widget = Pattern "bricks"; + + ResizeType resizet = class + Option_string "Resize type" [ + "resize", + "scale", + "sample", + "adaptive-resize" + ] resizet { + + _flag = resizet; + + Option_edit caption labels value = this.ResizeType labels?value; + } + ResizeType_widget = ResizeType "resize"; + + Size_widget = class { + _vislevel = 3; + + width = Expression "Width (pixels)" 64; + height = Expression "Height (pixels)" 64; + + _flag = "-size " ++ + print width.expr ++ "x" ++ print height.expr; + + }; + + StatType statt = class + Option_string "Statistic type" [ + "Gradient", + "Maximum", + "Mean", + "Median", + "Minimum", + "Mode", + "Nonpeak", + "StandardDeviation" + ] statt { + + _flag = statt; + + Option_edit caption labels value = this.StatType labels?value; + } + StatType_widget = StatType "Mean"; + + VirtualPixel vp = class + Option_string "Virtual pixel" [ + "Background", + "Black", + "CheckerTile", + "Dither", + "Edge", + "Gray", + "HorizontalTile", + "HorizontalTileEdge", + "Mirror", + "None", + "Random", + "Tile", + "Transparent", + "VerticalTile", + "VerticalTileEdge", + "White" + ] vp { + + _flag = "-virtual-pixel " ++ vp; + + _isBackground = (vp == "Background"); + + Option_edit caption labels value = this.VirtualPixel labels?value; + } + VirtualPixel_widget = VirtualPixel "Edge"; + + VirtualPixelBack_widget = class { + virtpix = Magick.VirtualPixel_widget; + background = Magick.background_widget; + _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") + ++ virtpix._flag; + } + + Geometry_widget = class { + _vislevel = 3; + + x = Expression "X" 0; + y = Expression "Y" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + AnnotGeometry_widget = class { + _vislevel = 3; + + shearX = Expression "shear X (degrees)" 0; + shearY = Expression "shear Y (degrees)" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print shearX.expr, "x", print shearY.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + OffsetGeometry_widget = class { + _vislevel = 3; + + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag = concat [format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + WhxyGeometry_widget = class { + _vislevel = 3; + + x = Expression "Width" 0; + y = Expression "Height" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + FrameGeometry_widget = class { + _vislevel = 3; + + x = Expression "Width" 0; + y = Expression "Height" 0; + outbev = Expression "Outer bevel thickness" 0; + inbev = Expression "Inner bevel thickness" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format outbev, format inbev] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + Font_widget = class { + _vislevel = 3; + + family = Option_string "Family" [ + "Arial", + "ArialBlack", + "AvantGarde", + "BitstreamCharter", + "Bookman", + "CenturySchoolbook", + "ComicSansMS", + "Courier", + "CourierNew", + "DejaVuSans", + "DejaVuSansMono", + "DejaVuSerif", + "Dingbats", + "FreeMono", + "FreeSans", + "FreeSerif", + "Garuda", + "Georgia", + "Helvetica", + "HelveticaNarrow", + "Impact", + "LiberationMono", + "LiberationSans", + "LiberationSerif", + "NewCenturySchlbk", + "Palatino", + "Purisa", + "Symbol", + "Times", + "TimesNewRoman", + "Ubuntu", + "Verdana", + "Webdings" + ] "Arial"; + style = Option_string "Style" [ + "Any", "Italic", "Normal", "Oblique" + ] "Normal"; + weight = Scale "Weight" 1 800 400; + size = Scale "Point size" 1 100 12; + stretch = Option_string "Stretch" [ + "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", + "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", + "UltraExpanded" + ] "Normal"; + + _flag = join_sep " " [ + "-family", family.item, + "-weight", print weight.value, + "-pointsize", print size.value, + "-style", style.item, + "-stretch", stretch.item]; + } +} + diff --git a/share/nip2/compat/8.5/_predicate.def b/share/nip2/compat/8.5/_predicate.def new file mode 100644 index 00000000..14872802 --- /dev/null +++ b/share/nip2/compat/8.5/_predicate.def @@ -0,0 +1,528 @@ + +/* is_colour_space str: is a string one of nip's colour space names + */ +is_colour_space str = Image_type.colour_spaces.present 0 str; + +/* is_colour_type n: is a number one of VIPS's colour spaces + */ +is_colour_type n = Image_type.colour_spaces.present 1 n; + +/* is_number: is a real or a complex number. + */ +is_number a = is_real a || is_complex a; + +/* is_int: is an integer + */ +is_int a = is_real a && a == (int) a; + +/* is_uint: is an unsigned integer + */ +is_uint a = is_int a && a >= 0; + +/* is_pint: is a positive integer + */ +is_pint a = is_int a && a > 0; + +/* is_preal: is a positive real + */ +is_preal a = is_real a && a > 0; + +/* is_ureal: is an unsigned real + */ +is_ureal a = is_real a && a >= 0; + +/* is_letter c: true if character c is an ASCII letter + * + * is_letter :: char -> bool + */ +is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + +/* is_digit c: true if character c is an ASCII digit + * + * is_digit :: char->bool + */ +is_digit x = '0' <= x && x <= '9'; + +/* A whitespace character. + * + * is_space :: char->bool + */ +is_space = member " \n\t"; + +/* List str starts with section prefix. + * + * is_prefix "hell" "hello world!" == true + * is_prefix :: [*] -> [*] -> bool + */ +is_prefix prefix str = take (len prefix) str == prefix; + +/* List str ends with section suffix. + * + * is_suffix "ld!" "hello world!" == true + * is_suffix :: [*] -> [*] -> bool + */ +is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; + +/* List contains seqence. + * + * is_substr "llo" "hello world!" == true + * is_substr :: [*] -> [*] -> bool + */ +is_substr seq str = any (map (is_prefix seq) (iterate tl str)); + +/* is_listof p s: true if finite list with p true for every element. + */ +is_listof p l = is_list l && all (map p l); + +/* is_string s: true if finite list of char. + */ +is_string s = is_listof is_char s; + +/* is_real_list l: is l a list of real numbers ... test each element, + * so no infinite lists pls. + */ +is_real_list l = is_listof is_real l; + +/* is_string_list l: is l a finite list of finite strings. + */ +is_string_list l = is_listof is_string l; + +/* Test list length ... quicker than len x == n for large lists. + */ +is_list_len n x + = true, x == [] && n == 0 + = false, x == [] || n == 0 + = is_list_len (n - 1) (tl x); + +is_list_len_more n x + = true, x != [] && n == 0 + = false, x == [] || n == 0 + = is_list_len_more (n - 1) (tl x); + +is_list_len_more_equal n x + = true, n == 0 + = false, x == [] + = is_list_len_more_equal (n - 1) (tl x); + +/* is_rectangular l: is l a rectangular data structure + */ +is_rectangular l + = true, !is_list l + = true, all (map is_obj l) + = true, all (map is_list l) && + all (map (not @ is_obj) l) && + all (map is_rectangular l) && + is_list_len_more 0 l && + all (map (is_list_len (len (hd l))) (tl l)) + = false +{ + // treat strings as a base type, not [char] + is_obj x = !is_list x || is_string x; +} + +/* is_matrix l: is l a list of lists of real numbers, all the same length + * + * [[]] is the empty matrix, [] is the empty list ... disallow [] + */ +is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; + +/* is_square_matrix l: is l a matrix with width == height + */ +is_square_matrix l + = true, l == [[]] + = is_matrix l && is_list_len (len (hd l)) l; + +/* is_oddmatrix l: is l a matrix with odd-length sides + */ +is_oddmatrix l + = true, l == [[]] + = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; + +/* is_odd_square_matrix l: is l a square_matrix with odd-length sides + */ +is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; + +/* Is an item in a column of a table? + */ +is_incolumn n table x = member (map (extract n) table) x; + +/* Is HGuide or VGuide. + */ +is_HGuide x = is_instanceof "HGuide" x; + +is_VGuide x = is_instanceof "VGuide" x; + +is_Guide x = is_HGuide x || is_VGuide x; + +is_Mark x = is_instanceof "Mark" x; + +is_Group x = is_instanceof "Group" x; + +is_NULL x = is_instanceof "NULL" x; + +is_List x = is_instanceof "List" x; + +is_Image x = is_instanceof "Image" x; + +is_Plot x = is_instanceof "Plot" x; + +is_Region x = is_instanceof "Region" x; + +is_Real x = is_instanceof "Real" x; + +is_Matrix x = is_instanceof "Matrix_base" x; + +is_Vector x = is_instanceof "Vector" x; + +is_Colour x = is_instanceof "Colour" x; + +is_Arrow x = is_instanceof "Arrow" x; + +is_Bool x = is_instanceof "Bool" x; + +is_Scale x = is_instanceof "Scale" x; + +is_Rect x = is_instanceof "Rect" x; + +is_Number x = is_instanceof "Number" x; + +is_Expression x = is_instanceof "Expression" x; + +is_String x = is_instanceof "String" x; + +/* A list of the form [[1,2],[3,4],[5,6]...] + */ +is_xy_list l + = is_list l && all (map xy l) +{ + xy l = is_real_list l && is_list_len 2 l; +} + +// does a nested list structure contain a Group object? +contains_Group l + = true, is_list l && any (map is_Group l) + = any (map contains_Group l), is_list l + = false; + +/* Does an object have a sensible VIPS type? + */ +has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; + +/* Try to get a VIPS image type from an object. + */ +get_type x + = get_type_im x, is_image x + = get_type_im x.value, is_Image x + = get_type_im x.image.value, is_Arrow x + = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x + // slightly odd ... but our display is always 0-255, so it makes sense for + // a plain number to be in the same range + = Image_type.sRGB, is_real x + = oo_unary_function get_type_op x, is_class x + = error (_ "bad arguments to " ++ "get_type") +{ + get_type_op = Operator "get_type" get_type + Operator_type.COMPOUND false; + + // get the type from a VIPS image ... but only if it makes sense with + // the rest of the image + + // we often have Type set wrong, hence the ugly guessing :-( + // can have alpha, hence we let bands be one more than you might think + + get_type_im im + = Image_type.LABQ, coding == Image_coding.LABPACK + = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 + = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && + (width == 1 || height == 1) + = Image_type.B_W, is_bands 1 + = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 + = type, is_colorimetric && is_bands 3 + = Image_type.sRGB, !is_colorimetric && is_bands 3 + = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 + = type + { + type = get_header "Type" im; + coding = get_header "Coding" im; + bands = get_header "Bands" im; + width = get_header "Xsize" im; + height = get_header "Ysize" im; + + // 3-band colorimetric types we allow ... the things which the + // Colour/Convert To menu can make, excluding mono. + ok_types = [ + Image_type.sRGB, + Image_type.RGB16, + Image_type.LAB, + Image_type.LABQ, + Image_type.LABS, + Image_type.LCH, + Image_type.XYZ, + Image_type.YXY, + Image_type.UCS + ]; + is_colorimetric = member ok_types type; + + // is bands n, with an optional alpha (ie. can be n + 1 too) + is_bands n = bands == n || bands == n + 1; + } +} + +has_format x = has_member "format" x || is_Arrow x || is_image x; + +get_format x + = x.format, has_member "format" x + = x.image.format, is_Arrow x + = get_header "BandFmt" x, is_image x + = oo_unary_function get_format_op x, is_class x + = error (_ "bad arguments to " ++ "get_format") +{ + get_format_op = Operator "get_format" get_format + Operator_type.COMPOUND false; +} + +has_bits x = has_member "bits" x || is_Arrow x || is_image x; + +get_bits x + = x.bits, has_member "bits" x + = x.image.bits, is_Arrow x + = get_header "Bbits" x, is_image x + = oo_unary_function get_bits_op x, is_class x + = error (_ "bad arguments to " ++ "get_bits") +{ + get_bits_op = Operator "get_bits" get_format + Operator_type.COMPOUND false; +} + +has_bands x = is_image x || has_member "bands" x || is_Arrow x; + +get_bands x + = x.bands, has_member "bands" x + = x.image.bands, is_Arrow x + = get_header "Bands" x, is_image x + = 1, is_real x + = len x, is_real_list x + = oo_unary_function get_bands_op x, is_class x + = error (_ "bad arguments to " ++ "get_bands") +{ + get_bands_op = Operator "get_bands" get_bands + Operator_type.COMPOUND false; +} + +has_coding x = has_member "coding" x || is_Arrow x || is_image x; + +get_coding x + = x.coding, has_member "coding" x + = x.image.coding, is_Arrow x + = get_header "Coding" x, is_image x + = Image_coding.NOCODING, is_real x + = oo_unary_function get_coding_op x, is_class x + = error (_ "bad arguments to " ++ "get_coding") +{ + get_coding_op = Operator "get_coding" get_coding + Operator_type.COMPOUND false; +} + +has_xres x = has_member "xres" x || is_Arrow x || is_image x; + +get_xres x + = x.xres, has_member "xres" x + = x.image.xres, is_Arrow x + = get_header "Xres" x, is_image x + = oo_unary_function get_xres_op x, is_class x + = error (_ "bad arguments to " ++ "get_xres") +{ + get_xres_op = Operator "get_xres" get_xres + Operator_type.COMPOUND false; +} + +has_yres x = has_member "yres" x || is_Arrow x || is_image x; + +get_yres x + = x.yres, has_member "yres" x + = x.image.yres, is_Arrow x + = get_header "Yres" x, is_image x + = oo_unary_function get_yres_op x, is_class x + = error (_ "bad arguments to " ++ "get_yres") +{ + get_yres_op = Operator "get_yres" get_yres + Operator_type.COMPOUND false; +} + +has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; + +get_xoffset x + = x.xoffset, has_member "xoffset" x + = x.image.xoffset, is_Arrow x + = get_header "Xoffset" x, is_image x + = oo_unary_function get_xoffset_op x, is_class x + = error (_ "bad arguments to " ++ "get_xoffset") +{ + get_xoffset_op = Operator "get_xoffset" get_xoffset + Operator_type.COMPOUND false; +} + +has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; + +get_yoffset x + = x.yoffset, has_member "yoffset" x + = x.image.yoffset, is_Arrow x + = get_header "Yoffset" x, is_image x + = oo_unary_function get_yoffset_op x, is_class x + = error (_ "bad arguments to " ++ "get_yoffset") +{ + get_yoffset_op = Operator "get_yoffset" get_yoffset + Operator_type.COMPOUND false; +} + +has_value = has_member "value"; + +get_value x = x.value; + +has_image x = is_image x || is_Image x || is_Arrow x; + +get_image x + = x.value, is_Image x + = x.image.value, is_Arrow x + = x, is_image x + = oo_unary_function get_image_op x, is_class x + = error (_ "bad arguments to " ++ "get_image") +{ + get_image_op = Operator "get_image" get_image + Operator_type.COMPOUND false; +} + +has_number x = is_number x || is_Real x; + +get_number x + = x.value, is_Real x + = x, is_number x + = oo_unary_function get_number_op x, is_class x + = error (_ "bad arguments to " ++ "get_number") +{ + get_number_op = Operator "get_number" get_number + Operator_type.COMPOUND false; +} + +has_real x = is_real x || is_Real x; + +get_real x + = x.value, is_Real x + = x, is_real x + = oo_unary_function get_real_op x, is_class x + = error (_ "bad arguments to " ++ "get_real") +{ + get_real_op = Operator "get_real" get_real + Operator_type.COMPOUND false; +} + +has_width x = has_member "width" x || is_image x; + +get_width x + = x.width, has_member "width" x + = get_header "Xsize" x, is_image x + = oo_unary_function get_width_op x, is_class x + = error (_ "bad arguments to " ++ "get_width") +{ + get_width_op = Operator "get_width" get_width + Operator_type.COMPOUND false; +} + +has_height x = has_member "height" x || is_image x; + +get_height x + = x.height, has_member "height" x + = get_header "Ysize" x, is_image x + = oo_unary_function get_height_op x, is_class x + = error (_ "bad arguments to " ++ "get_height") +{ + get_height_op = Operator "get_height" get_height + Operator_type.COMPOUND false; +} + +has_left x = has_member "left" x; + +get_left x + = x.left, has_member "left" x + = oo_unary_function get_left_op x, is_class x + = error (_ "bad arguments to " ++ "get_left") +{ + get_left_op = Operator "get_left" get_left + Operator_type.COMPOUND false; +} + +has_top x = has_member "top" x; + +get_top x + = x.top, has_member "top" x + = oo_unary_function get_top_op x, is_class x + = error (_ "bad arguments to " ++ "get_top") +{ + get_top_op = Operator "get_top" get_top + Operator_type.COMPOUND false; +} + +// like has/get member, but first in a lst of objects +has_member_list has objects + = filter has objects != []; + +// need one with the args swapped +get_member = converse dot; + +// get a member from the first of a list of objects to have it +get_member_list has get objects + = hd members, members != [] + = error "unable to get property" +{ + members = map get (filter has objects); +} + +is_hist x + = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) +{ + im = get_image x; + w = get_width im; + h = get_height im; + t = get_type im; +} + +get_header field x + = oo_unary_function get_header_op x, is_class x + = get_header_image x, is_image x + = error (_ "bad arguments to " ++ "get_header") +{ + get_header_op = Operator "get_header" (get_header field) + Operator_type.COMPOUND false; + get_header_image im + = im_header_int field im, type == itype + = im_header_double field im, type == dtype + = im_header_string field im, type == stype1 || type == stype2 + = error (_ "image has no field " ++ field), type == 0 + = error (_ "unknown type for field " ++ field) + { + type = im_header_get_typeof field im; + + itype = name2gtype "gint"; + dtype = name2gtype "gdouble"; + stype1 = name2gtype "VipsRefString"; + stype2 = name2gtype "gchararray"; + } +} + +get_header_type field x + = oo_unary_function get_header_type_op x, is_class x + = im_header_get_typeof field x, is_image x + = error (_ "bad arguments to " ++ "get_header_type") +{ + get_header_type_op = Operator "get_header_type" (get_header_type field) + Operator_type.COMPOUND false; +} + +set_header field value x + = oo_unary_function set_header_op x, is_class x + = im_copy_set_meta x field value, is_image x + = error (_ "bad arguments to " ++ "set_header") +{ + set_header_op = Operator "set_header" (set_header field value) + Operator_type.COMPOUND false; +} diff --git a/share/nip2/compat/8.5/_stdenv.def b/share/nip2/compat/8.5/_stdenv.def new file mode 100644 index 00000000..a7cf8f3a --- /dev/null +++ b/share/nip2/compat/8.5/_stdenv.def @@ -0,0 +1,2597 @@ +/* optional args to functions + */ + +get_option options defaults f + = error (_ "unknown parameter " ++ f), hits == [] + = hits?0 +{ + hits = [v :: [n, v] <- options ++ defaults; n == f]; +} + +/* Various operators as functions. + */ + +logical_and a b = a && b; +logical_or a b = a || b; +bitwise_and a b = a & b; +bitwise_or a b = a | b; +eor a b = a ^ b; +left_shift a b = a << b; +right_shift a b = a >> b; +not a = !a; + +less a b = a < b; +more a b = a > b; +less_equal a b = a <= b; +more_equal a b = a >= b; +equal a b = a == b; +not_equal a b = a != b; +pointer_equal a b = a === b; +not_pointer_equal a b = a !== b; + +add a b = a + b; +subtract a b = a - b; +multiply a b = a * b; +divide a b = a / b; +idivide a b = (int) ((int) a / (int) b); +power a b = a ** b; +square x = x * x; +remainder a b = a % b; + +cons a b = a : b; +dot a b = a . ( b ); +join a b = a ++ b; +// 'difference' is defined in _list +subscript a b = a ? b; + +generate s n f = [s, n .. f]; +comma r i = (r, i); + +compose f g = f @ g; + +// our only trinary operator is actually a binary operator +if_then_else a x = if a then x?0 else x?1; + +cast_unsigned_char x = (unsigned char) x; +cast_signed_char x = (signed char) x; +cast_unsigned_short x = (unsigned short) x; +cast_signed_short x = (signed short) x; +cast_unsigned_int x = (unsigned int) x; +cast_signed_int x = (signed int) x; +cast_float x = (float) x; +cast_double x = (double) x; +cast_complex x = (complex) x; +cast_double_complex x = (double complex) x; + +unary_minus x = -x; +negate x = !x; +complement x = ~x; +unary_plus x = +x; + +// the function we call for "a -> v" expressions +mksvpair s v + = [s, v], is_string s + = error "not str on lhs of ->"; + +// the vector ops ... im is an image, vec is a real_list +vec op_name im vec + = im_lintra_vec ones im vec, + op_name == "add" || op_name == "add'" + = im_lintra_vec ones (-1 * im) vec, + op_name == "subtract'" + = im_lintra_vec ones im inv, + op_name == "subtract" + = im_lintra_vec vec im zeros, + op_name == "multiply" || op_name == "multiply'" + = im_lintra_vec vec (1 / im) zeros, + op_name == "divide'" + = im_lintra_vec recip im zeros, + op_name == "divide" + = im_expntra_vec im vec, + op_name == "power'" + = im_powtra_vec im vec, + op_name == "power" + = im_remainderconst_vec im vec, + op_name == "remainder" + = im_andimage_vec im vec, + op_name == "bitwise_and" || op_name == "bitwise_and'" + = im_orimage_vec im vec, + op_name == "bitwise_or" || op_name == "bitwise_or'" + = im_eorimage_vec im vec, + op_name == "eor" || op_name == "eor'" + = im_equal_vec im vec, + op_name == "equal" || op_name == "equal'" + = im_notequal_vec im vec, + op_name == "not_equal" || op_name == "not_equal'" + = im_less_vec im vec, + op_name == "less" + = im_moreeq_vec im vec, + op_name == "less'" + = im_lesseq_vec im vec, + op_name == "less_equal" + = im_more_vec im vec, + op_name == "less_equal'" + = error ("unimplemented vector operation: " ++ op_name) +{ + zeros = replicate (len vec) 0; + ones = replicate (len vec) 1; + recip = map (divide 1) vec; + inv = map (multiply (-1)) vec; +} + +// make a name value pair +mknvpair n v + = [n, v], is_string n + = error "not [char] on LHS of =>"; + +/* Macbeth chart patch names. + */ +macbeth_names = [ + "Dark skin", + "Light skin", + "Blue sky", + "Foliage", + "Blue flower", + "Bluish green", + "Orange", + "Purplish blue", + "Moderate red", + "Purple", + "Yellow green", + "Orange yellow", + "Blue", + "Green", + "Red", + "Yellow", + "Magenta", + "Cyan", + "White (density 0.05)", + "Neutral 8 (density 0.23)", + "Neutral 6.5 (density 0.44)", + "Neutral 5 (density 0.70)", + "Neutral 3.5 (density 1.05)", + "Black (density 1.50)" +]; + +bandsplit x + = oo_unary_function bandsplit_op x, is_class x + = map (subscript x) [0 .. bands - 1], is_image x + = error (_ "bad arguments to " ++ "bandsplit") +{ + bands = get_header "Bands" x; + bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) + Operator_type.COMPOUND false; +} + +bandjoin l + = wrapper joined, has_wrapper + = joined, is_listof has_image l + = error (_ "bad arguments to " ++ "bandjoin") +{ + has_wrapper = has_member_list (has_member "Image") l; + wrapper = get_member_list (has_member "Image") (get_member "Image") l; + joined = im_gbandjoin (map get_image l); +} + +bandand x + = oo_unary_function bandand_op x, is_class x + = foldr1 bitwise_and (bandsplit x), is_image x + = error (_ "bad arguments to " ++ "bandand") +{ + bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; +} + +bandor x + = oo_unary_function bandor_op x, is_class x + = foldr1 bitwise_or (bandsplit x), is_image x + = error (_ "bad arguments to " ++ "bandor") +{ + bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; +} + +sum x + = oo_unary_function sum_op x, is_class x + = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x + = sum_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") +{ + sum_op = Operator "sum" sum Operator_type.COMPOUND false; + + // add elements in a nested-list thing + sum_list l + = foldr acc 0 l + { + acc x total + = total + sum x, is_list x + = total + x; + } +} + +product x + = oo_unary_function product_op x, is_class x + = product_list x, is_list x + // (product image) doesn't make much sense :( + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") +{ + product_op = Operator "product" product Operator_type.COMPOUND false; + + product_list l + = foldr prod 1 l + { + prod x total + = total * product x, is_list x + = total * x; + } +} + +mean x + = oo_unary_function mean_op x, is_class x + = im_avg x, is_image x + = mean_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") +{ + mean_op = Operator "mean" mean Operator_type.COMPOUND false; + + mean_list l = sum l / size l; + + // number of elements in some sort of nested-list thing + size l + = foldr acc 0 l + { + acc x total + = total + size x, is_list x + = total + 1; + } +} + +meang x + = (appl (power e) @ mean @ appl log) x +{ + appl fn x + = map fn x, is_list x + = fn x; +} + +skew x + = oo_unary_function skew_op x, is_class x + = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x + = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") +{ + skew_op = Operator "skew" skew Operator_type.COMPOUND false; + + // squash any large matrix down to a flat list ... much simpler + x' + = x, is_image x; + = flatten x; + + m = mean x'; + s = deviation x'; + w = get_width x'; + h = get_height x'; + b = get_bands x'; + + N + = w * h * b, is_image x' + = len x'; +} + +kurtosis x + = oo_unary_function kurtosis_op x, is_class x + = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x + = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") +{ + kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; + + // squash any large matrix down to a flat list ... much simpler + x' + = x, is_image x; + = flatten x; + + m = mean x'; + s = deviation x'; + w = get_width x'; + h = get_height x'; + b = get_bands x'; + + N + = len x', is_list x'; + = w * h * b; +} + +// zero-excluding mean +meanze x + = oo_unary_function meanze_op x, is_class x + = meanze_image_hist x, is_image x && + (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) + = meanze_image x, is_image x + = meanze_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") +{ + fmt = get_format x; + + meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; + + meanze_list l = sum l / size l; + + // number of non-zero elements in some sort of nested-list thing + size l + = foldr acc 0 l + { + acc x total + = total + size x, is_list x + = total + 1, x != 0; + = total; + } + + // add elements in a nested-list thing + sum l + = foldr acc 0 l + { + acc x total + = total + sum x, is_list x + = total + x; + } + + // image mean, for any image type + meanze_image i + = sum / N + { + w = get_width i; + h = get_height i; + b = get_bands i; + + st = stats i; + sum = st.value?0?2; + + // find non-zero pixels (not zero in all bands) + zp = im_notequal_vec i (replicate b 0); + + // number of non-zero pixels + N = b * (mean zp * w * h) / 255; + } + + // image mean for 8 and 16-bit unsigned images + // we can use a histogram, yay, and save a pass through the image + meanze_image_hist i + = sum / N + { + // histogram, knock out zeros + hist = hist_find i; + black = image_new 1 1 (get_bands hist) + (get_format hist) (get_coding hist) (get_type hist) 0 0 0; + histze = insert 0 0 black hist; + + // matching identity + iden + = im_identity_ushort (get_bands hist) (get_width hist), + (get_width hist) > 256 + = im_identity (get_bands hist); + + // number of non-zero pixels + N = mean histze * 256; + + // sum of pixels + sum = mean (hist * iden) * 256; + } +} + +deviation x + = oo_unary_function deviation_op x, is_class x + = im_deviate x, is_image x + = deviation_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "deviation") +{ + deviation_op = Operator "deviation" + deviation Operator_type.COMPOUND false; + + deviation_list l + = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 + { + [n, s, s2] = sum_sum2_list l; + } + + // return n, sum, sum of squares for a list of reals + sum_sum2_list x + = foldr accumulate [0, 0, 0] x + { + accumulate x sofar + = [n + 1, x + s, x * x + s2], is_real x + = [n + n', s + s', s2 + s2'], is_list x + = error "sum_sum2_list: not real or [real]" + { + [n, s, s2] = sofar; + [n', s', s2'] = sum_sum2_list x; + } + } +} + +deviationze x + = oo_unary_function deviationze_op x, is_class x + = deviationze_image x, is_image x + = deviationze_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "deviationze") +{ + deviationze_op = Operator "deviationze" + deviationze Operator_type.COMPOUND false; + + deviationze_list l + = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 + { + [n, s, s2] = sum_sum2_list l; + } + + // return number of non-zero elements, sum, sum of squares for a list of + // reals + sum_sum2_list x + = foldr accumulate [0, 0, 0] x + { + accumulate x sofar + = sofar, is_real x && x == 0 + = [n + 1, x + s, x * x + s2], is_real x + = [n + n', s + s', s2 + s2'], is_list x + = error "sum_sum2_list: not real or [real]" + { + [n, s, s2] = sofar; + [n', s', s2'] = sum_sum2_list x; + } + } + + deviationze_image i + = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 + { + w = get_width i; + h = get_height i; + b = get_bands i; + + st = stats i; + sum = st.value?0?2; + sum2 = st.value?0?3; + + // find non-zero pixels (not zero in all bands) + zp = im_notequal_vec i (replicate b 0); + + // number of non-zero pixels + N = b * (mean zp * w * h) / 255; + } +} + +// find the centre of gravity of a histogram +gravity x + = oo_unary_function gravity_op x, is_class x + = im_hist_gravity x, is_hist x + = gravity_list x, is_list x + = error (_ "bad arguments to " ++ "gravity") +{ + gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; + + // centre of gravity of a histogram... use the histogram to weight an + // identity, then sum, then find the mean element + im_hist_gravity h + = m + { + // make horizontal + h' + = rot270 h, get_width h == 1 + = h, get_height h == 1 + = error "width or height not 1"; + + // number of elements + w = get_width h'; + + // matching identity + i + = im_identity_ushort 1 w, w <= 2 ** 16 - 1 + = make_xy w 1 ? 0; + + // weight identity and sum + s = mean (i * h') * w; + + // sum of original histogram + s' = mean h * w; + + // weighted mean + m = s / s'; + } + + gravity_list l + = m + { + w = len l; + + // matching identity + i = [0, 1 .. w - 1]; + + // weight identity and sum + s = sum (map2 multiply i l); + + // sum of original histogram + s' = sum l; + + // weighted mean + m = s / s'; + } +} + +project x + = oo_unary_function project_op x, is_class x + = im_project x, is_image x + = error (_ "bad arguments to " ++ "project") +{ + project_op = Operator "project" project Operator_type.COMPOUND false; +} + +abs x + = oo_unary_function abs_op x, is_class x + = im_abs x, is_image x + = abs_cmplx x, is_complex x + = abs_num x, is_real x + = abs_list x, is_real_list x + = abs_list (map abs_list x), is_matrix x + = error (_ "bad arguments to " ++ "abs") +{ + abs_op = Operator "abs" abs Operator_type.COMPOUND false; + + abs_list l = (sum (map square l)) ** 0.5; + + abs_num n + = n, n >= 0 + = -n; + + abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; +} + +copy x + = oo_unary_function copy_op x, is_class x + = im_copy x, is_image x + = x +{ + copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; +} + +// like abs, but treat pixels as vectors ... ie. always get a 1-band image +// back ... also treat matricies as lists of vectors +// handy for dE from object difference +abs_vec x + = oo_unary_function abs_vec_op x, is_class x + = abs_vec_image x, is_image x + = abs_vec_cmplx x, is_complex x + = abs_vec_num x, is_real x + = abs_vec_list x, is_real_list x + = mean (map abs_vec_list x), is_matrix x + = error (_ "bad arguments to " ++ "abs_vec") +{ + abs_vec_op = Operator "abs_vec" + abs_vec Operator_type.COMPOUND false; + + abs_vec_list l = (sum (map square l)) ** 0.5; + + abs_vec_num n + = n, n >= 0 + = -n; + + abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; + + abs_vec_image im + = (sum (map square (bandsplit im))) ** 0.5; +} + +transpose x + = oo_unary_function transpose_op x, is_class x + = transpose_image x, is_image x + = transpose_list x, is_listof is_list x + = error (_ "bad arguments to " ++ "transpose") +{ + transpose_op = Operator "transpose" + transpose Operator_type.COMPOUND_REWRAP false; + + transpose_list l + = [], l' == [] + = (map hd l') : (transpose_list (map tl l')) + { + l' = takewhile (not_equal []) l; + } + + transpose_image = im_flipver @ im_rot270; +} + +rot45 x + = oo_unary_function rot45_op x, is_class x + = error "rot45 image: not implemented", is_image x + = error (_ "bad arguments to " ++ "rot45") +{ + rot45_op = Operator "rot45" + rot45_object Operator_type.COMPOUND_REWRAP false; + + rot45_object x + = rot45_matrix x, is_odd_square_matrix x + = error "rot45 image: not implemented", is_image x + = error (_ "bad arguments to " ++ "rot45"); + + // slow, but what the heck + rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; +} + +// apply an image function to a [[real]] ... matrix is converted to a 1 band +// image for processing +apply_matrix_as_image fn m + = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; + +// a general image/matrix operation where the mat version is most easily done +// by converting mat->image->mat +apply_matim_operation name fn x + = oo_unary_function class_op x, is_class x + = fn x, is_image x + = apply_matrix_as_image fn x, is_matrix x + = error (_ "bad arguments to " ++ name) +{ + class_op = Operator name + (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; +} + +rot90 = apply_matim_operation "rot90" im_rot90; +rot180 = apply_matim_operation "rot180" im_rot180; +rot270 = apply_matim_operation "rot270" im_rot270; +rotquad = apply_matim_operation "rotquad" im_rotquad; +fliplr = apply_matim_operation "fliplr" im_fliphor; +fliptb = apply_matim_operation "flipud" im_flipver; + +image_set_type type x + = oo_unary_function image_set_type_op x, is_class x + = im_copy_set x (to_real type) + (get_header "Xres" x) (get_header "Yres" x) + (get_header "Xoffset" x) (get_header "Yoffset" x), + is_image x + = error (_ "bad arguments to " ++ "image_set_type:" ++ + print type ++ " " ++ print x) +{ + image_set_type_op = Operator "image_set_type" + (image_set_type type) Operator_type.COMPOUND_REWRAP false; +} + +image_set_origin xoff yoff x + = oo_unary_function image_set_origin_op x, is_class x + = im_copy_set x + (get_header "Type" x) + (get_header "Xres" x) (get_header "Yres" x) + (to_real xoff) (to_real yoff), + is_image x + = error (_ "bad arguments to " ++ "image_set_origin") +{ + image_set_origin_op = Operator "image_set_origin" + (image_set_origin xoff yoff) + Operator_type.COMPOUND_REWRAP false; +} + +cache tile_width tile_height max_tiles x + = oo_unary_function cache_op x, is_class x + = im_tile_cache_random x (to_real tile_width) (to_real tile_height) + (to_real max_tiles), is_image x + = error (_ "bad arguments to " ++ "cache") +{ + cache_op = Operator "cache" + (cache tile_width tile_height max_tiles) + Operator_type.COMPOUND_REWRAP false; +} + +tile across down x + = oo_unary_function tile_op x, is_class x + = im_replicate x (to_real across) (to_real down), is_image x + = error (_ "bad arguments to " ++ "tile") +{ + tile_op = Operator "tile" + (tile across down) Operator_type.COMPOUND_REWRAP false; +} + +grid tile_height across down x + = oo_unary_function grid_op x, is_class x + = im_grid x (to_real tile_height) (to_real across) (to_real down), + is_image x + = error (_ "bad arguments to " ++ "grid") +{ + grid_op = Operator "grid" + (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; +} + +max_pair a b + = a, a > b + = b; + +min_pair a b + = a, a < b + = b; + +range min value max = min_pair max (max_pair min value); + +max x + = oo_unary_function max_op x, is_class x + = im_max x, is_image x + = max_list x, is_list x + = x, is_number x + = error (_ "bad arguments to " ++ "max") +{ + max_op = Operator "max" max Operator_type.COMPOUND false; + + max_list x + = error "max []", x == [] + = foldr1 max_pair x, is_real_list x + = foldr1 max_pair (map max_list x), is_list x + = max x; +} + +min x + = oo_unary_function min_op x, is_class x + = im_min x, is_image x + = min_list x, is_list x + = x, is_number x + = error (_ "bad arguments to " ++ "min") +{ + min_op = Operator "min" min Operator_type.COMPOUND false; + + min_list x + = error "min []", x == [] + = foldr1 min_pair x, is_real_list x + = foldr1 min_pair (map min_list x), is_list x + = min x; +} + +maxpos x + = oo_unary_function maxpos_op x, is_class x + = im_maxpos x, is_image x + = maxpos_matrix x, is_matrix x + = maxpos_list x, is_list x + = error (_ "bad arguments to " ++ "maxpos") +{ + maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; + + maxpos_matrix m + = (-1, -1), m == [[]] + = (indexes?row, row) + { + max_value = max (Matrix m); + indexes = map (index (equal max_value)) m; + row = index (not_equal (-1)) indexes; + } + + maxpos_list l + = -1, l == [] + = index (equal (max l)) l; +} + +minpos x + = oo_unary_function minpos_op x, is_class x + = im_minpos x, is_image x + = minpos_matrix x, is_matrix x + = minpos_list x, is_list x + = error (_ "bad arguments to " ++ "minpos") +{ + minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; + + minpos_matrix m + = (-1, -1), m == [[]] + = (indexes?row, row) + { + min_value = min (Matrix m); + indexes = map (index (equal min_value)) m; + row = index (not_equal (-1)) indexes; + } + + minpos_list l + = -1, l == [] + = index (equal (min l)) l; +} + +stats x + = oo_unary_function stats_op x, is_class x + = im_stats x, is_image x + = im_stats (to_image x).value, is_matrix x + = error (_ "bad arguments to " ++ "stats") +{ + stats_op = Operator "stats" + stats Operator_type.COMPOUND false; +} + +e = 2.7182818284590452354; + +pi = 3.14159265358979323846; + +rad d = 2 * pi * (d / 360); + +deg r = 360 * r / (2 * pi); + +sign x + = oo_unary_function sign_op x, is_class x + = im_sign x, is_image x + = sign_cmplx x, is_complex x + = sign_num x, is_real x + = error (_ "bad arguments to " ++ "sign") +{ + sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; + + sign_num n + = 0, n == 0 + = 1, n > 0 + = -1; + + sign_cmplx c + = (0, 0), mod == 0 + = (re c / mod, im c / mod) + { + mod = abs c; + } +} + +rint x + = oo_unary_function rint_op x, is_class x + = im_rint x, is_image x + = rint_value x, is_number x + = error (_ "bad arguments to " ++ "rint") +{ + rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; + + rint_value x + = (int) (x + 0.5), x > 0 + = (int) (x - 0.5); +} + +scale x + = oo_unary_function scale_op x, is_class x + = (unsigned char) x, is_number x + = im_scale x, is_image x + = scale_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "scale") +{ + scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; + + scale_list l + = apply_scale s o l + { + mn = find_limit min_pair l; + mx = find_limit max_pair l; + s = 255.0 / (mx - mn); + o = -(mn * s); + } + + find_limit fn l + = find_limit fn (map (find_limit fn) l), is_listof is_list l + = foldr1 fn l; + + apply_scale s o x + = x * s + o, is_number x + = map (apply_scale s o) x; +} + +scaleps x + = oo_unary_function scale_op x, is_class x + = im_scaleps x, is_image x + = error (_ "bad arguments to " ++ "scale") +{ + scale_op = Operator "scaleps" + scaleps Operator_type.COMPOUND_REWRAP false; +} + +fwfft x + = oo_unary_function fwfft_op x, is_class x + = im_fwfft x, is_image x + = error (_ "bad arguments to " ++ "fwfft") +{ + fwfft_op = Operator "fwfft" + fwfft Operator_type.COMPOUND_REWRAP false; +} + +invfft x + = oo_unary_function invfft_op x, is_class x + = im_invfftr x, is_image x + = error (_ "bad arguments to " ++ "invfft") +{ + invfft_op = Operator "invfft" + invfft Operator_type.COMPOUND_REWRAP false; +} + +falsecolour x + = oo_unary_function falsecolour_op x, is_class x + = image_set_type Image_type.sRGB (im_falsecolour x), is_image x + = error (_ "bad arguments to " ++ "falsecolour") +{ + falsecolour_op = Operator "falsecolour" + falsecolour Operator_type.COMPOUND_REWRAP false; +} + +polar x + = oo_unary_function polar_op x, is_class x + = im_c2amph x, is_image x + = polar_cmplx x, is_complex x + = error (_ "bad arguments to " ++ "polar") +{ + polar_op = Operator "polar" polar Operator_type.COMPOUND false; + + polar_cmplx r + = (l, a) + { + a + = 270, x == 0 && y < 0 + = 90, x == 0 && y >= 0 + = 360 + atan (y / x), x > 0 && y < 0 + = atan (y / x), x > 0 && y >= 0 + = 180 + atan (y / x); + + l = (x ** 2 + y ** 2) ** 0.5; + + x = re r; + y = im r; + } +} + +rectangular x + = oo_unary_function rectangular_op x, is_class x + = im_c2rect x, is_image x + = rectangular_cmplx x, is_complex x + = error (_ "bad arguments to " ++ "rectangular") +{ + rectangular_op = Operator "rectangular" + rectangular Operator_type.COMPOUND false; + + rectangular_cmplx p + = (x, y) + { + l = re p; + a = im p; + + x = l * cos a; + y = l * sin a; + } +} + +// we can't use colour_unary: that likes 3 band only +recomb matrix x + = oo_unary_function recomb_op x, is_class x + = im_recomb x matrix, is_image x + = recomb_real_list x, is_real_list x + = map recomb_real_list x, is_matrix x + = error (_ "bad arguments to " ++ "recomb") +{ + // COMPOUND_REWRAP ... signal to the colour class to go to image and + // back + recomb_op = Operator "recomb" + (recomb matrix) Operator_type.COMPOUND_REWRAP false; + + // process [1,2,3 ..] as an image + recomb_real_list l + = (to_matrix im').value?0 + { + im = (float) (to_image (Vector l)).value; + im' = recomb matrix im; + } +} + +extract_area x y w h obj + = oo_unary_function extract_area_op obj, is_class obj + = im_extract_area obj x' y' w' h', is_image obj + = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj + = error (_ "bad arguments to " ++ "extract_area") +{ + x' = to_real x; + y' = to_real y; + w' = to_real w; + h' = to_real h; + + extract_area_op = Operator "extract_area" (extract_area x y w h) + Operator_type.COMPOUND_REWRAP false; + + extract_range from length list + = (take length @ drop from) list; +} + +extract_band b obj = subscript obj b; + +extract_row y obj + = oo_unary_function extract_row_op obj, is_class obj + = extract_area 0 y' (get_width obj) 1 obj, is_image obj + = [obj?y'], is_matrix obj + = error (_ "bad arguments to " ++ "extract_row") +{ + y' = to_real y; + + extract_row_op = Operator "extract_row" (extract_row y) + Operator_type.COMPOUND_REWRAP false; +} + +extract_column x obj + = oo_unary_function extract_column_op obj, is_class obj + = extract_area x' 0 1 height obj, is_image obj + = map (\row [row?x']) obj, is_matrix obj + = error (_ "bad arguments to " ++ "extract_column") +{ + x' = to_real x; + height = get_header "Ysize" obj; + + extract_column_op = Operator "extract_column" (extract_column x) + Operator_type.COMPOUND_REWRAP false; +} + +blend cond in1 in2 + = oo_binary_function blend_op cond [in1,in2], is_class cond + = im_blend (get_image cond) (get_image in1) (get_image in2), + has_image cond && has_image in1 && has_image in2 + = error (_ "bad arguments to " ++ "blend" ++ ": " ++ + join_sep ", " (map print [cond, in1, in2])) +{ + blend_op = Operator "blend" + blend_obj Operator_type.COMPOUND_REWRAP false; + + blend_obj cond x + = blend_result_image + { + [then_part, else_part] = x; + + // get things about our output from inputs in this order + objects = [then_part, else_part, cond]; + + // properties of our output image + target_width = get_member_list has_width get_width objects; + target_height = get_member_list has_height get_height objects; + target_bands = get_member_list has_bands get_bands objects; + target_format = get_member_list has_format get_format objects; + target_type = get_member_list has_type get_type objects; + + to_image x + = to_image_size target_width target_height + target_bands target_format x; + + [then_image, else_image] = map to_image [then_part, else_part]; + + blend_result_image = image_set_type target_type + (im_blend cond then_image else_image); + } +} + +// do big first: we want to keep big's class, if possible +// eg. big is a Plot, small is a 1x1 Image +insert x y small big + = oo_binary'_function insert_op small big, is_class big + = oo_binary_function insert_op small big, is_class small + = im_insert big small (to_real x) (to_real y), + is_image small && is_image big + = error (_ "bad arguments to " ++ "insert") +{ + insert_op = Operator "insert" + (insert x y) Operator_type.COMPOUND_REWRAP false; +} + +insert_noexpand x y small big + = oo_binary_function insert_noexpand_op small big, is_class small + = oo_binary'_function insert_noexpand_op small big, is_class big + = im_insert_noexpand big small (to_real x) (to_real y), + is_image small && is_image big + = error (_ "bad arguments to " ++ "insert_noexpand") +{ + insert_noexpand_op = Operator "insert_noexpand" + (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; +} + +decode im + = oo_unary_function decode_op im, is_class im + = decode_im im, is_image im + = error (_ "bad arguments to " ++ "decode") +{ + decode_op = Operator "decode" + decode Operator_type.COMPOUND_REWRAP false; + + decode_im im + = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK + = im_rad2float im, get_coding im == Image_coding.RAD + = im; +} + +measure_draw across down measure image + = mark +{ + patch_width = image.width / across; + sample_width = patch_width * (measure / 100); + left_margin = (patch_width - sample_width) / 2; + patch_height = image.height / down; + sample_height = patch_height * (measure / 100); + top_margin = (patch_height - sample_height) / 2; + + cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: + y <- [0 .. down - 1]; x <- [0 .. across - 1]]; + x = map (extract 0) cods; + y = map (extract 1) cods; + + outer = mkim [$pixel => 255] sample_width sample_height 1; + inner = mkim [] (sample_width - 4) (sample_height - 4) 1; + patch = insert 2 2 inner outer; + + bg = mkim [] image.width image.height 1; + + mask = Image (im_insertset bg.value patch.value x y); + + image' = colour_transform_to Image_type.sRGB image; + + mark = if mask then Vector [0, 255, 0] else image'; +} + +measure_sample across down measure image + = measures +{ + patch_width = image.width / across; + sample_width = patch_width * (measure / 100); + left_margin = (patch_width - sample_width) / 2; + patch_height = image.height / down; + sample_height = patch_height * (measure / 100); + top_margin = (patch_height - sample_height) / 2; + + cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: + y <- [0 .. down - 1]; x <- [0 .. across - 1]]; + + image' = decode image; + patches = map (\p extract_area p?0 p?1 sample_width sample_height image') + cods; + measures = Matrix (map (map mean) (map bandsplit patches)); +} + +extract_bands b n obj + = oo_unary_function extract_bands_op obj, is_class obj + = im_extract_bands obj (to_real b) (to_real n), is_image obj + = error (_ "bad arguments to " ++ "extract_bands") +{ + extract_bands_op = Operator "extract_bands" + (extract_bands b n) Operator_type.COMPOUND_REWRAP false; +} + +invert x + = oo_unary_function invert_op x, is_class x + = im_invert x, is_image x + = 255 - x, is_real x + = error (_ "bad arguments to " ++ "invert") +{ + invert_op = Operator "invert" invert Operator_type.COMPOUND false; +} + +transform ipol wrap params image + = oo_unary_function transform_op image, is_class image + = im_transform image + (to_matrix params) (to_real ipol) (to_real wrap), is_image image + = error (_ "bad arguments to " ++ "transform") +{ + transform_op = Operator "transform" + (transform ipol wrap params) + Operator_type.COMPOUND_REWRAP false; +} + +transform_search max_error max_iterations order ipol wrap sample reference + = oo_binary_function transform_search_op sample reference, is_class sample + = oo_binary'_function transform_search_op sample reference, + is_class reference + = im_transform_search sample reference + (to_real max_error) (to_real max_iterations) (to_real order) + (to_real ipol) (to_real wrap), + is_image sample && is_image reference + = error (_ "bad arguments to " ++ "transform_search") +{ + transform_search_op = Operator "transform_search" + (transform_search max_error max_iterations order ipol wrap) + Operator_type.COMPOUND false; +} + +rotate interp angle image + = oo_binary_function rotate_op angle image, is_class angle + = oo_binary'_function rotate_op angle image, is_class image + = im_affinei_all image interp.value a (-b) b a 0 0, + is_real angle && is_image image + = error (_ "bad arguments to " ++ "rotate") +{ + rotate_op = Operator "rotate" + (rotate interp) Operator_type.COMPOUND_REWRAP false; + a = cos angle; + b = sin angle; +} + +matrix_binary fn a b + = itom (fn (mtoi a) (mtoi b)) +{ + mtoi x = im_mask2vips (Matrix x); + itom x = (im_vips2mask x).value; +} + +join_lr a b + = oo_binary_function join_lr_op a b, is_class a + = oo_binary'_function join_lr_op a b, is_class b + = join_im a b, is_image a && is_image b + = matrix_binary join_im a b, is_matrix a && is_matrix b + = error (_ "bad arguments to " ++ "join_lr") +{ + join_lr_op = Operator "join_lr" + join_lr Operator_type.COMPOUND_REWRAP false; + + join_im a b = insert (get_width a) 0 b a; +} + +join_tb a b + = oo_binary_function join_tb_op a b, is_class a + = oo_binary'_function join_tb_op a b, is_class b + = join_im a b, is_image a && is_image b + = matrix_binary join_im a b, is_matrix a && is_matrix b + = error (_ "bad arguments to " ++ "join_tb") +{ + join_tb_op = Operator "join_tb" + join_tb Operator_type.COMPOUND_REWRAP false; + + join_im a b = insert 0 (get_height a) b a; +} + +conj x + = oo_unary_function conj_op x, is_class x + = (re x, -im x), + is_complex x || + (is_image x && format == Image_format.COMPLEX) || + (is_image x && format == Image_format.DPCOMPLEX) + // assume it's some sort of real + = x +{ + format = get_header "BandFmt" x; + conj_op = Operator "conj" conj Operator_type.COMPOUND false; +} + +clip2fmt format image + = oo_unary_function clip2fmt_op image, is_class image + = im_clip2fmt image (to_real format), is_image image + = error (_ "bad arguments to " ++ "clip2fmt") +{ + clip2fmt_op = Operator "clip2fmt" + (clip2fmt format) Operator_type.COMPOUND_REWRAP false; +} + +embed type x y w h im + = oo_unary_function embed_op im, is_class im + = im_embed im (to_real type) + (to_real x) (to_real y) (to_real w) (to_real h), is_image im + = error (_ "bad arguments to " ++ "embed") +{ + embed_op = Operator "embed" + (embed type x y w h) Operator_type.COMPOUND_REWRAP false; +} + +/* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it + * with m1, turn it back to a matrix again. + */ +_morph_2_masks fn m1 m2 + = m'' +{ + // The [[real]] can contain 128 values ... squeeze them out! + image = im_mask2vips (Matrix m2) == 255; + m2_width = get_width image; + m2_height = get_height image; + + // need to embed m2 in an image large enough for us to be able to + // position m1 all around the edges, with a 1 pixel overlap + image' = embed 0 + (m1.width / 2) (m1.height / 2) + (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) + image; + + // morph! + image'' = fn m1 image'; + + // back to mask + m' = im_vips2mask ((double) image''); + + // Turn 0 in output to 128 (don't care). + m'' + = map (map fn) m'.value + { + fn a + = 128, a == 0; + = a; + } +} + +dilate mask image + = oo_unary_function dilate_op image, is_class image + = im_dilate image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "dilate") +{ + dilate_op = Operator "dilate" + dilate_object Operator_type.COMPOUND_REWRAP false; + + dilate_object x + = _morph_2_masks dilate mask x, is_matrix x + = dilate mask x; +} + +erode mask image + = oo_unary_function erode_op image, is_class image + = im_erode image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "erode") +{ + erode_op = Operator "erode" + erode_object Operator_type.COMPOUND_REWRAP false; + + erode_object x + = _morph_2_masks erode mask x, is_matrix x + = erode mask x; +} + +conv mask image + = oo_unary_function conv_op image, is_class image + = im_conv image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "conv" ++ ": " ++ + "conv (" ++ print mask ++ ") (" ++ print image ++ ")") +{ + conv_op = Operator "conv" + (conv mask) Operator_type.COMPOUND_REWRAP false; +} + +convf mask image + = oo_unary_function convf_op image, is_class image + = im_conv_f image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convf" ++ ": " ++ + "convf (" ++ print mask ++ ") (" ++ print image ++ ")") +{ + convf_op = Operator "convf" + (convf mask) Operator_type.COMPOUND_REWRAP false; +} + +convsep mask image + = oo_unary_function convsep_op image, is_class image + = im_convsep image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convsep") +{ + convsep_op = Operator "convsep" + (convsep mask) Operator_type.COMPOUND_REWRAP false; +} + +aconvsep layers mask image + = oo_unary_function aconvsep_op image, is_class image + = im_aconvsep image (to_matrix mask) (to_real layers), is_image image + = error (_ "bad arguments to " ++ "aconvsep") +{ + aconvsep_op = Operator "aconvsep" + (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; +} + +convsepf mask image + = oo_unary_function convsepf_op image, is_class image + = im_convsep_f image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convsepf") +{ + convsepf_op = Operator "convsepf" + (convsepf mask) Operator_type.COMPOUND_REWRAP false; +} + +rank w h n image + = oo_unary_function rank_op image, is_class image + = im_rank image (to_real w) (to_real h) (to_real n), is_image image + = error (_ "bad arguments to " ++ "rank") +{ + rank_op = Operator "rank" + (rank w h n) Operator_type.COMPOUND_REWRAP false; +} + +rank_image n x + = rlist x.value, is_Group x + = rlist x, is_list x + = error (_ "bad arguments to " ++ "rank_image") +{ + rlist l + = wrapper ranked, has_wrapper + = ranked + { + has_wrapper = has_member_list (has_member "Image") l; + wrapper = get_member_list (has_member "Image") (get_member "Image") l; + ranked = im_rank_image (map get_image l) (to_real n); + } +} + +// find the correlation surface for a small image within a big one +correlate small big + = oo_binary_function correlate_op small big, is_class small + = oo_binary'_function correlate_op small big, is_class big + = im_spcor big small, is_image small && is_image big + = error (_ "bad arguments to " ++ "correlate") +{ + correlate_op = Operator "correlate" + correlate Operator_type.COMPOUND_REWRAP false; +} + +// just sum-of-squares-of-differences +correlate_fast small big + = oo_binary_function correlate_fast_op small big, is_class small + = oo_binary'_function correlate_fast_op small big, is_class big + = im_fastcor big small, is_image small && is_image big + = error (_ "bad arguments to " ++ "correlate_fast") +{ + correlate_fast_op = Operator "correlate_fast" + correlate_fast Operator_type.COMPOUND_REWRAP false; +} + +// set Type, wrap as Plot_hist if it's a class +hist_tag x + = oo_unary_function hist_tag_op x, is_class x + = image_set_type Image_type.HISTOGRAM x, is_image x + = error (_ "bad arguments to " ++ "hist_tag") +{ + hist_tag_op = Operator "hist_tag" + (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; +} + +hist_find x + = oo_unary_function hist_find_op x, is_class x + = im_histgr x (-1), is_image x + = error (_ "bad arguments to " ++ "hist_find") +{ + hist_find_op = Operator "hist_find" + (Plot_histogram @ hist_find) Operator_type.COMPOUND false; +} + +hist_find_nD bins image + = oo_unary_function hist_find_nD_op image, is_class image + = im_histnD image (to_real bins), is_image image + = error (_ "bad arguments to " ++ "hist_find_nD") +{ + hist_find_nD_op = Operator "hist_find_nD" + (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; +} + +hist_find_indexed index value + = oo_binary_function hist_find_indexed_op index value, is_class index + = oo_binary'_function hist_find_indexed_op index value, is_class value + = im_hist_indexed index value, is_image index && is_image value + = error (_ "bad arguments to " ++ "hist_find_indexed") +{ + hist_find_indexed_op = Operator "hist_find_indexed" + (compose (compose Plot_histogram) hist_find_indexed) + Operator_type.COMPOUND false; +} + +hist_map hist image + = oo_binary_function hist_map_op hist image, is_class hist + = oo_binary'_function hist_map_op hist image, is_class image + = im_maplut image hist, is_image hist && is_image image + = error (_ "bad arguments to " ++ "hist_map") +{ + // can't use rewrap, as we want to always wrap as image + hist_map_op = Operator "hist_map" + (compose (compose Image) hist_map) Operator_type.COMPOUND false; +} + +hist_cum hist + = oo_unary_function hist_cum_op hist, is_class hist + = im_histcum hist, is_image hist + = error (_ "bad arguments to " ++ "hist_cum") +{ + hist_cum_op = Operator "hist_cum" + hist_cum Operator_type.COMPOUND_REWRAP false; +} + +hist_diff hist + = oo_unary_function hist_diff_op hist, is_class hist + = im_histdiff hist, is_image hist + = error (_ "bad arguments to " ++ "hist_diff") +{ + hist_diff_op = Operator "hist_diff" + hist_diff Operator_type.COMPOUND_REWRAP false; + + im_histdiff h + = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h + { + // up the format so it can represent the whole range of + // possible values from this mask + fmt x + = Image_format.SHORT, + x == Image_format.UCHAR || x == Image_format.CHAR + = Image_format.INT, + x == Image_format.USHORT || x == Image_format.SHORT || + x == Image_format.UINT + = x; + } +} + +hist_norm hist + = oo_unary_function hist_norm_op hist, is_class hist + = im_histnorm hist, is_image hist + = error (_ "bad arguments to " ++ "hist_norm") +{ + hist_norm_op = Operator "hist_norm" + hist_norm Operator_type.COMPOUND_REWRAP false; +} + +hist_inv hist + = oo_unary_function hist_inv_op hist, is_class hist + = inv hist, is_image hist + = error (_ "bad arguments to " ++ "hist_inv") +{ + hist_inv_op = Operator "hist_inv" + hist_inv Operator_type.COMPOUND_REWRAP false; + + inv im + = im_invertlut (to_matrix im''') len + { + // need a vertical doublemask + im' + = rot90 im, get_width im > 1 && get_height im == 1 + = im, get_width im == 1 && get_height im > 1 + = error (_ "not a hist"); + len = get_height im'; + + // values must be scaled to 0 - 1 + im'' = im' / (max im'); + + // add an index column on the left + // again, must be in 0-1 + y = ((make_xy 1 len)?1) / len; + im''' = y ++ im''; + } +} + +hist_match in ref + = oo_binary_function hist_match_op in ref, is_class in + = oo_binary'_function hist_match_op in ref, is_class ref + = im_histspec in ref, is_image in && is_image ref + = error (_ "bad arguments to " ++ "hist_match") +{ + hist_match_op = Operator "hist_match" + hist_match Operator_type.COMPOUND_REWRAP false; +} + +hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; + +hist_equalize_local w h l image + = oo_unary_function hist_equalize_local_op image, is_class image + = out, is_image image + = error (_ "bad arguments to " ++ "hist_equalize_local") +{ + hist_equalize_local_op = Operator "hist_equalize_local" + (hist_equalize_local w h l) Operator_type.COMPOUND_REWRAP false; + + [out] = vips_call "hist_local" + [image, to_real w, to_real h] [$max_slope => to_real l]; +} + +// find the threshold below which are percent of the image (percent in [0,1]) +// eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels +hist_thresh percent image + = x +{ + // our own normaliser ... we don't want to norm channels separately + // norm to [0,1] + my_hist_norm h = h / max h; + + // normalised cumulative hist + // we sum the channels before we normalise, because we want to treat them + // all the same + h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) + image.value; + + // threshold that, then use im_profile to search for the x position in the + // histogram + x = mean (im_profile (h > percent) 1); +} + +/* Sometimes useful, despite plotting now being built in, for making + * diagnostic images. + */ +hist_plot hist + = oo_unary_function hist_plot_op hist, is_class hist + = im_histplot hist, is_image hist + = error (_ "bad arguments to " ++ "hist_plot") +{ + hist_plot_op = Operator "hist_plot" + (Image @ hist_plot) Operator_type.COMPOUND false; +} + +zerox d x + = oo_unary_function zerox_op x, is_class x + = im_zerox x (to_real d), is_image x + = error (_ "bad arguments to " ++ "zerox") +{ + zerox_op = Operator "zerox" + (zerox d) Operator_type.COMPOUND_REWRAP false; +} + +reduce kernel xshr yshr image + = oo_unary_function reduce_op image, is_class image + = reduce_im image, is_image image + = error (_ "bad arguments to " ++ "reduce") +{ + reduce_op = Operator "reduce" + reduce_im Operator_type.COMPOUND_REWRAP false; + + reduce_im im + = out + { + [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; + } +} + +similarity interpolate scale angle image + = oo_unary_function similarity_op image, is_class image + = similarity_im image, is_image image + = error (_ "bad arguments to " ++ "similarity") +{ + similarity_op = Operator "similarity" + similarity_im Operator_type.COMPOUND_REWRAP false; + + similarity_im im + = out + { + [out] = vips_call "similarity" [im] [ + $interpolate => interpolate.value, + $scale => scale, + $angle => angle + ]; + } +} + +resize kernel xfac yfac image + = oo_unary_function resize_op image, is_class image + = resize_im image, is_image image + = error (_ "bad arguments to " ++ "resize") +{ + resize_op = Operator "resize" + resize_im Operator_type.COMPOUND_REWRAP false; + + xfac' = to_real xfac; + yfac' = to_real yfac; + + rxfac' = 1 / xfac'; + ryfac' = 1 / yfac'; + + is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; + + resize_im im + // upscale by integer factor, nearest neighbour + = im_zoom im xfac' yfac', + is_int xfac' && is_int yfac' && + xfac' >= 1 && yfac' >= 1 && + is_nn + + // downscale by integer factor, nearest neighbour + = im_subsample im rxfac' ryfac', + is_int rxfac' && is_int ryfac' && + rxfac' >= 1 && ryfac' >= 1 && + is_nn + + // everything else ... we just pass on to vips_resize() + = vips_resize kernel xfac' yfac' im + { + vips_resize kernel hscale vscale im + = out + { + [out] = vips_call "resize" [im, hscale] + [$vscale => vscale, $kernel => kernel.value]; + } + } +} + +sharpen radius x1 y2 y3 m1 m2 in + = oo_unary_function sharpen_op in, is_class in + = im_sharpen in (to_real radius) + (to_real x1) (to_real y2) (to_real y3) + (to_real m1) (to_real m2), is_image in + = error (_ "bad arguments to " ++ "sharpen") +{ + sharpen_op = Operator "sharpen" + (sharpen radius x1 y2 y3 m1 m2) + Operator_type.COMPOUND_REWRAP false; +} + +tone_analyse s m h sa ma ha in + = oo_unary_function tone_analyse_op in, is_class in + = im_tone_analyse in + (to_real s) (to_real m) (to_real h) + (to_real sa) (to_real ma) (to_real ha), is_image in + = error (_ "bad arguments to " ++ "tone_analyse") +{ + tone_analyse_op = Operator "tone_analyse" + (Plot_histogram @ tone_analyse s m h sa ma ha) + Operator_type.COMPOUND false; +} + +tone_map hist image + = oo_binary_function tone_map_op hist image, is_class hist + = oo_binary'_function tone_map_op hist image, is_class image + = im_tone_map image hist, is_image hist && is_image image + = error (_ "bad arguments to " ++ "tone_map") +{ + tone_map_op = Operator "tone_map" + tone_map Operator_type.COMPOUND_REWRAP false; +} + +tone_build fmt b w s m h sa ma ha + = (Plot_histogram @ clip2fmt fmt) + (im_tone_build_range mx mx + (to_real b) (to_real w) + (to_real s) (to_real m) (to_real h) + (to_real sa) (to_real ma) (to_real ha)) +{ + mx = Image_format.maxval fmt; +} + +icc_export depth profile intent in + = oo_unary_function icc_export_op in, is_class in + = im_icc_export_depth in + (to_real depth) (expand profile) (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_export") +{ + icc_export_op = Operator "icc_export" + (icc_export depth profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_import profile intent in + = oo_unary_function icc_import_op in, is_class in + = im_icc_import in + (expand profile) (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_import") +{ + icc_import_op = Operator "icc_import" + (icc_import profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_import_embedded intent in + = oo_unary_function icc_import_embedded_op in, is_class in + = im_icc_import_embedded in (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_import_embedded") +{ + icc_import_embedded_op = Operator "icc_import_embedded" + (icc_import_embedded intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_transform in_profile out_profile intent in + = oo_unary_function icc_transform_op in, is_class in + = im_icc_transform in + (expand in_profile) (expand out_profile) + (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_transform") +{ + icc_transform_op = Operator "icc_transform" + (icc_transform in_profile out_profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_ac2rc profile in + = oo_unary_function icc_ac2rc_op in, is_class in + = im_icc_ac2rc in (expand profile), is_image in + = error (_ "bad arguments to " ++ "icc_ac2rc") +{ + icc_ac2rc_op = Operator "icc_ac2rc" + (icc_ac2rc profile) + Operator_type.COMPOUND_REWRAP false; +} + + +// Given a xywh rect, flip it around so wh are always positive +rect_normalise x y w h + = [x', y', w', h'] +{ + x' + = x + w, w < 0 + = x; + y' + = y + h, h < 0 + = y; + w' = abs w; + h' = abs h; +} + +draw_flood_blob x y ink image + = oo_unary_function draw_flood_blob_op image, is_class image + = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image + = error (_ "bad arguments to " ++ "draw_flood_blob") +{ + draw_flood_blob_op = Operator "draw_flood_blob" + (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_flood x y ink image + = oo_unary_function draw_flood_op image, is_class image + = im_draw_flood image (to_real x) (to_real y) ink, is_image image + = error (_ "bad arguments to " ++ "draw_flood") +{ + draw_flood_op = Operator "draw_flood" + (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; +} + +/* This version of draw_rect uses insert_noexpand and will be fast, even for + * huge images. + */ +draw_rect_width x y w h f t ink image + = oo_unary_function draw_rect_width_op image, is_class image + = my_draw_rect_width image (to_int x) (to_int y) + (to_int w) (to_int h) (to_int f) (to_int t) ink, + is_image image + = error (_ "bad arguments to " ++ "draw_rect_width") +{ + draw_rect_width_op = Operator "draw_rect_width" + (draw_rect_width x y w h f t ink) + Operator_type.COMPOUND_REWRAP false; + + my_draw_rect_width image x y w h f t ink + = insert x' y' (block w' h') image, f == 1 + = (insert x' y' (block w' t) @ + insert (x' + w' - t) y' (block t h') @ + insert x' (y' + h' - t) (block w' t) @ + insert x' y' (block t h')) image + { + insert = insert_noexpand; + block w h = image_new w h (get_bands image) (get_format image) + (get_coding image) (get_type image) ink' 0 0; + ink' + = Vector ink, is_list ink + = ink; + [x', y', w', h'] = rect_normalise x y w h; + } +} + +/* Default to 1 pixel wide edges. + */ +draw_rect x y w h f ink image + = draw_rect_width x y w h f 1 ink image; + +/* This version of draw_rect uses the paintbox rect draw operation. It is an + * inplace operation and will use bucketloads of memory. + */ +draw_rect_paintbox x y w h f ink image + = oo_unary_function draw_rect_op image, is_class image + = im_draw_rect image (to_real x) (to_real y) + (to_real w) (to_real h) (to_real f) ink, is_image image + = error (_ "bad arguments to " ++ "draw_rect_paintbox") +{ + draw_rect_op = Operator "draw_rect" + (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_circle x y r f ink image + = oo_unary_function draw_circle_op image, is_class image + = im_draw_circle image (to_real x) (to_real y) + (to_real r) (to_real f) ink, is_image image + = error (_ "bad arguments to " ++ "draw_circle") +{ + draw_circle_op = Operator "draw_circle" + (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_line x1 y1 x2 y2 ink image + = oo_unary_function draw_line_op image, is_class image + = im_draw_line image (to_real x1) (to_real y1) + (to_real x2) (to_real y2) ink, is_image image + = error (_ "bad arguments to " ++ "draw_line") +{ + draw_line_op = Operator "draw_line" + (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; +} + +print_base base in + = oo_unary_function print_base_op in, is_class in + = map (print_base base) in, is_list in + = print_base_real, is_real in + = error (_ "bad arguments to " ++ "print_base") +{ + print_base_op + = Operator "print_base" (print_base base) Operator_type.COMPOUND false; + + print_base_real + = error "print_base: bad base", base < 2 || base > 16 + = "0", in < 0 || chars == [] + = reverse chars + { + digits = map (\x x % base) + (takewhile (not_equal 0) + (iterate (\x idivide x base) in)); + chars = map tohd digits; + + tohd x + = (char) ((int) '0' + x), x < 10 + = (char) ((int) 'A' + (x - 10)); + } +} + +/* id x: the identity function + * + * id :: * -> * + */ +id x = x; + +/* const x y: junk y, return x + * + * (const 3) is the function that always returns 3. + * const :: * -> ** -> * + */ +const x y = x; + +/* converse fn a b: swap order of args to fn + * + * converse fn a b == fn b a + * converse :: (* -> ** -> ***) -> ** -> * -> *** + */ +converse fn a b = fn b a; + +/* fix fn x: find the fixed point of a function + */ +fix fn x = limit (iterate fn x); + +/* until pred fn n: apply fn to n until pred succeeds; return that value + * + * until (more 1000) (multiply 2) 1 = 1024 + * until :: (* -> bool) -> (* -> *) -> * -> * + */ +until pred fn n + = n, pred n + = until pred fn (fn n); + +/* Infinite list of primes. + */ +primes + = 1 : (sieve [2 ..]) +{ + sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); + nmultiple n x = x / n != (int) (x / n); +} + +/* Map an n-ary function (pass the args as a list) over groups of objects. + * The objects can be single objects or groups. If more than one + * object is a group, we iterate for the length of the smallest group. + * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. + * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the + * output and don't apply the function. + + copy-pasted into _types, keep in sync + + */ +map_nary fn args + = fn args, groups == [] + = Group (map process [0, 1 .. shortest - 1]) +{ + // find all the group arguments + groups = filter is_Group args; + + // what's the length of the shortest group arg? + shortest = foldr1 min_pair (map (len @ get_value) groups); + + // process index n ... pull that member from each argument + // recurse to handle application, so we work for nested groups too + process n + = NULL, any (map (is_noval n) args) + = map_nary fn (map (extract n) args) + { + extract n arg + = arg.value?n, is_Group arg + = arg; + + is_noval n arg = is_Group arg && arg.value?n == NULL; + } +} + +/* Map a 1-ary function over an object. + */ +map_unary fn a = map_nary (list_1ary fn) [a]; + +/* Map a 2-ary function over a pair of objects. + */ +map_binary fn a b = map_nary (list_2ary fn) [a, b]; + +/* Map a 3-ary function over three objects. + */ +map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; + +/* Map a 4-ary function over three objects. + */ +map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; + +/* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on + * vectors and matricies. + */ +map_nary_list fn args + = fn args, lists == [] + = map process [0, 1 .. shortest - 1] +{ + // find all the list arguments + lists = filter is_list args; + + // what's the length of the shortest list arg? + shortest = foldr1 min_pair (map len lists); + + // process index n ... pull that member from each argument + // recurse to handle application, so we work for nested lists too + process n + = map_nary_list fn (map (extract n) args) + { + extract n arg + = arg?n, is_list arg + = arg; + } +} + +map_unaryl fn a = map_nary_list (list_1ary fn) [a]; + +map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; + +/* Remove features smaller than x pixels across from an image. This used to be + * rather complex ... convsep is now good enough to use. + */ +smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; + +/* Chop up an image into a list of lists of smaller images. Pad edges with + * black. + */ +imagearray_chop tile_width tile_height hoverlap voverlap i + = map chop' [0, vstep .. last_y] +{ + width = get_width i; + height = get_height i; + bands = get_bands i; + format = get_format i; + type = get_type i; + + tile_width' = to_real tile_width; + tile_height' = to_real tile_height; + hoverlap' = to_real hoverlap; + voverlap' = to_real voverlap; + + /* Unique pixels per tile. + */ + hstep = tile_width' - hoverlap'; + vstep = tile_height' - voverlap'; + + /* Number of tiles across and down. Remember the case where width == + * hstep. + */ + across = (int) ((width - 1) / hstep) + 1; + down = (int) ((height - 1) / vstep) + 1; + + /* top/left of final tile. + */ + last_x = hstep * (across - 1); + last_y = vstep * (down - 1); + + /* How much do we need to pad by? + */ + sx = last_x + tile_width'; + sy = last_y + tile_height'; + + /* Expand image with black to pad size. + */ + pad = embed 0 0 0 sx sy i; + + /* Chop up a row. + */ + chop' y + = map chop'' [0, hstep .. last_x] + { + chop'' x = extract_area x y tile_width' tile_height' pad; + } +} + +/* Reassemble image. + */ +imagearray_assemble hoverlap voverlap il + = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il +{ + lrj l r = insert (get_width l + hoverlap) 0 r l; + tbj t b = insert 0 (get_height t + voverlap) b t; +} + +/* Generate an nxn identity matrix. + */ +identity_matrix n + = error "identity_matrix: n > 0", n < 1 + = map line [0 .. n - 1] +{ + line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; +} + +/* Incomplete gamma function Q(a, x) == 1 - P(a, x) + + FIXME ... this is now a builtin, until we can get a nice List class + (requires overloadable ':') + +gammq a x + = error "bad args", x < 0 || a <= 0 + = 1 - gamser, x < a + 1 + = gammcf +{ + gamser = (gser a x)?0; + gammcf = (gcf a x)?0; +} + */ + +/* Incomplete gamma function P(a, x) evaluated as series representation. Also + * return ln(gamma(a)) ... nr in c, pp 218 + */ +gser a x + = [gamser, gln] +{ + gln = gammln a; + gamser + = error "bad args", x < 0 + = 0, x == 0 + = 1 // fix this + { + // maximum iterations + maxit = 100; + + ap = List [a + 1, a + 2 ...]; + xoap = x / ap; + + del = map product (prefixes xoap.value); + + + + + + + +/* + del = map (multiply (1 / a)) (map product (prefixes xoap)) + + del = + + xap = iterate (multiply + */ + + /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, + * 3], [1, 2, 3, 4] ...] + */ + prefixes l = map (converse take l) [1..]; + + } +} + +/* ln(gamma(xx)) ... nr in c, pp 214 + */ +gammln xx + = gln +{ + cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, + -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; + y = take 6 (iterate (add 1) (xx + 1)); + ser = 1.000000000190015 + sum (map2 divide cof y); + tmp = xx + 0.5; + tmp' = tmp - ((xx + 0.5) * log tmp); + gln = -tmp + log (2.5066282746310005 * ser / xx); +} + +/* make a LUT from a scatter + */ +buildlut x + = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 + = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 + = error (_ "bad arguments to " ++ "buildlut"); + +/* Linear regression. Return a class with the stuff we need in. + * from s15.2, p 665 NR in C + * + * Also calculate R2, see eg.: + * https://en.wikipedia.org/wiki/Coefficient_of_determination + */ +linreg xes yes + = obj +{ + obj = class { + // in case we ever get shown in the workspace + _vislevel = 2; + + slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; + intercept = (sy - sx * slope) / ss; + + chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; + + siga = (chi2 / (ss - 2)) ** 0.5 * + ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; + sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; + + // for compat with linregw, see below + q = 1.0; + + R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; + } + + ss = len xes; + sx = sum xes; + sy = sum yes; + my = sy / ss; + sxoss = sx / ss; + + tes = [x - sxoss :: x <- xes]; + st2 = sum [t ** 2 :: t <- tes]; +} + +/* Weighted linear regression. Xes, yes and a list of deviations. + */ +linregw xes yes devs + = obj +{ + obj = class { + // in case we ever get shown in the workspace + _vislevel = 2; + + slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; + intercept = (sy - sx * slope) / ss; + + chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: + [x, y, sd] <- zip3 xes yes devs]; + + siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; + sigb = (1 / st2) ** 0.5; + + q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); + + R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; + } + + wt = [sd ** -0.5 :: sd <- devs]; + + ss = sum wt; + sx = sum [x * w :: [x, w] <- zip2 xes wt]; + sy = sum [y * w :: [y, w] <- zip2 yes wt]; + my = sy / len xes; + sxoss = sx / ss; + + tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; + st2 = sum [t ** 2 :: t <- tes]; +} + +/* Clustering: pass in a list of points, repeatedly merge the + * closest two points until no two points are closer than the threshold. + * Return [merged-points, corresponding-weights]. A weight is a list of the + * indexes we merged to make that point, ie. len weight == how significant + * this point is. + * + * eg. + * cluster 12 [152,154,155,42,159] == + * [[155,42],[[1,2,0,4],[3]]] + */ +cluster thresh points + = oo_unary_function cluster_op points, is_class points + // can't use [0..len points - 1], in case len points == 0 + = merge [points, map (converse cons []) (take (len points) [0 ..])], + is_list points + = error (_ "bad arguments to " ++ "cluster") +{ + cluster_op = Operator "cluster" + (cluster thresh) Operator_type.COMPOUND false; + + merge x + = x, m < 2 || d > thresh + = merge [points', weights'] + { + [points, weights] = x; + m = len points; + + // generate indexes of all possible pairs, avoiding comparing a thing + // to itself, and assuming that dist is reflexive + // first index is always less than 2nd index + // the +1,+2 makes sure we have an increasing generator, otherwise we + // can get [3 .. 4] (for example), which will make a decreasing + // sequence + pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; + + // distance function + // arg is eg. [3,1], meaning get distance from point 3 to point 1 + dist x + = abs (points?i - points?j) + { + [i, j] = x; + } + + // smallest distance, then the two points we merge + p = minpos (map dist pairs); + d = dist pairs?p; + [i, j] = pairs?p; + + // new point and new weight + nw = weights?i ++ weights?j; + np = (points?i * len weights?i + points?j * len weights?j) / len nw; + + // remove element i from a list + remove i l = take i l ++ drop (i + 1) l; + + // remove two old points, add the new merged one + // i < j (see "pairs", above) + points' = np : remove i (remove j points); + weights' = nw : remove i (remove j weights); + } +} + +/* Extract the area of an image around an arrow. + * Transform the image to make the arrow horizontal, then displace by hd and + * vd pxels, then cut out a bit h pixels high centered on the arrow. + */ +extract_arrow hd vd h arrow + = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' +{ + // the line as a polar vector + pv = polar (arrow.width, arrow.height); + a = im pv; + + // smallest rotation that will make the line horizontal + a' + = 360 - a, a > 270 + = 180 - a, a > 90 + = -a; + + im' = rotate Interpolate_bilinear a' arrow.image; + + // look at the start and end of the arrow, pick the leftmost + p + = (arrow.left, arrow.top), arrow.left <= arrow.right + = (arrow.right, arrow.bottom); + + // transform that point to im' space + p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); +} + +/* You'd think these would go in _convert, but they are not really colour ops, + * so put them here. + */ +rad2float image + = oo_unary_function rad2float_op image, is_class image + = im_rad2float image, is_image image + = error (_ "bad arguments to " ++ "rad2float") +{ + rad2float_op = Operator "rad2float" + rad2float Operator_type.COMPOUND_REWRAP false; +} + +float2rad image + = oo_unary_function float2rad_op image, is_class image + = im_float2rad image, is_image image + = error (_ "bad arguments to " ++ "float2rad") +{ + float2rad_op = Operator "float2rad" + float2rad Operator_type.COMPOUND_REWRAP false; +} + +segment x + = oo_unary_function segment_op x, is_class x + = image', is_image x + = error (_ "bad arguments to " ++ "segment") +{ + segment_op = Operator "segment" + segment Operator_type.COMPOUND_REWRAP false; + + [image, nsegs] = im_segment x; + image' = im_copy_set_meta image "n-segments" nsegs; +} + +point a b + = oo_binary_function point_op a b, is_class a + = oo_binary'_function point_op a b, is_class b + = im_read_point b x y, is_image b + = [b?x?y], is_matrix b + = [b?x], is_real_list b && y == 0 + = [b?y], is_real_list b && x == 0 + = error (_ "bad arguments to " ++ "point") +{ + point_op = Operator "point" + (\a\b Vector (point a b)) Operator_type.COMPOUND false; + + (x, y) + = a, is_complex a; + = (a?0, a?1), is_real_list a && is_list_len 2 a + = error "bad position format"; +} + +/* One image in, one out. + */ +system_image command x + = oo_unary_function system_image_op x, is_class x + = system x, is_image x + = error (_ "bad arguments to " ++ "system_image") +{ + system_image_op = Operator "system_image" + (system_image command) Operator_type.COMPOUND_REWRAP false; + + system im + = image_out + { + [image_out, log] = + im_system_image (get_image im) "%s.tif" "%s.tif" command; + } +} + +/* Two images in, one out. + */ +system_image2 command x1 x2 + = oo_binary_function system_image2_op x1 x2, is_class x1 + = oo_binary'_function system_image2_op x1 x2, is_class x2 + = system x1 x2, is_image x1 && is_image x2 + = error (_ "bad arguments to " ++ "system_image2") +{ + system_image2_op = Operator "system_image2" + (system_image2 command) Operator_type.COMPOUND_REWRAP false; + + system x1 x2 + = image_out + { + [image_out] = vips_call "system" [command] [ + $in => [x1, x2], + $out => true, + $out_format => "%s.tif", + $in_format => "%s.tif" + ]; + } +} + +/* Three images in, one out. + */ +system_image3 command x1 x2 x3 + = oo_binary_function system_image2_op x2 x3, is_class x2 + = oo_binary'_function system_image2_op x2 x3, is_class x3 + = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 + = error (_ "bad arguments to " ++ "system_image3") +{ + system_image2_op = Operator "system_image2" + (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; + + system x1 x2 x3 + = image_out + { + [image_out] = vips_call "system" [command] [ + $in => [x1, x2, x3], + $out => true, + $out_format => "%s.tif", + $in_format => "%s.tif" + ]; + } +} + +/* Zero images in, one out. + */ +system_image0 command + = Image image_out +{ + [image_out] = vips_call "system" [command] [ + $out => true, + $out_format => "%s.tif" + ]; +} + +hough_line w h x + = oo_unary_function hough_line_op x, is_class x + = hline (to_real w) (to_real h) x, is_image x + = error (_ "bad arguments to " ++ "hough_line") +{ + hough_line_op = Operator "hough_line" + (hough_line w h) Operator_type.COMPOUND_REWRAP false; + + hline w h x + = pspace + { + [pspace] = vips_call "hough_line" [x] [ + $width => w, + $height => h + ]; + } +} + +hough_circle s mn mx x + = oo_unary_function hough_circle_op x, is_class x + = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x + = error (_ "bad arguments to " ++ "hough_circle") +{ + hough_circle_op = Operator "hough_circle" + (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; + + hcircle s mn mx x + = pspace + { + [pspace] = vips_call "hough_circle" [x] [ + $scale => s, + $min_radius => mn, + $max_radius => mx + ]; + } +} + +mapim interp ind in + = oo_binary_function mapim_op ind in, is_class ind + = oo_binary'_function mapim_op ind in, is_class in + = mapim_fn ind in, is_image ind && is_image in + = error (_ "bad arguments to " ++ "mapim") +{ + mapim_op = Operator "mapim" + (mapim interp) Operator_type.COMPOUND_REWRAP false; + + mapim_fn ind im + = out + { + [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; + } +} + +perlin cell width height + = Image im +{ + [im] = vips_call "perlin" [to_real width, to_real height] [ + $cell_size => to_real cell + ]; +} + +worley cell width height + = Image im +{ + [im] = vips_call "worley" [to_real width, to_real height] [ + $cell_size => to_real cell + ]; +} + +gaussnoise width height mean sigma + = im +{ + [im] = vips_call "gaussnoise" [to_real width, to_real height] [ + $mean => to_real mean, + $sigma => to_real sigma + ]; +} + +flattenimage bg x + = oo_unary_function flatten_op x, is_class x + = flt (to_vector bg) x, is_image x + = error (_ "bad arguments to " ++ "flattenimage") +{ + flatten_op = Operator "flatten" + (flattenimage bg) Operator_type.COMPOUND_REWRAP false; + + flt bg x + = out + { + [out] = vips_call "flatten" [x] [ + $background => bg.value + ]; + } +} + +premultiply x + = oo_unary_function premultiply_op x, is_class x + = prem x, is_image x + = error (_ "bad arguments to " ++ "premultiply") +{ + premultiply_op = Operator "premultiply" + premultiply Operator_type.COMPOUND_REWRAP false; + + prem x + = out + { + [out] = vips_call "premultiply" [x] [ + ]; + } +} + +unpremultiply x + = oo_unary_function unpremultiply_op x, is_class x + = unprem x, is_image x + = error (_ "bad arguments to " ++ "unpremultiply") +{ + unpremultiply_op = Operator "unpremultiply" + unpremultiply Operator_type.COMPOUND_REWRAP false; + + unprem x + = out + { + [out] = vips_call "unpremultiply" [x] [ + ]; + } +} + +hist_entropy x + = oo_unary_function hist_entropy_op x, is_class x + = entropy x, is_image x + = error (_ "bad arguments to " ++ "hist_entropy") +{ + hist_entropy_op = Operator "hist_entropy" + hist_entropy Operator_type.COMPOUND_REWRAP false; + + entropy x + = out + { + [out] = vips_call "hist_entropy" [x] [ + ]; + } +} + + diff --git a/share/nip2/compat/8.5/_types.def b/share/nip2/compat/8.5/_types.def new file mode 100644 index 00000000..7516659b --- /dev/null +++ b/share/nip2/compat/8.5/_types.def @@ -0,0 +1,1285 @@ +/* A list of things. Do automatic iteration of unary and binary operators on + * us. + * List [1, 2] + [2, 3] -> List [3, 5] + * hd (List [2, 3]) -> 2 + * List [] == [] -> true + */ +List value = class + _Object { + _check_args = [ + [value, "value", check_list] + ]; + + // methods + oo_binary_table op x = [ + [apply2 op value x', + op.op_name == "subscript" || op.op_name == "subscript'" || + op.op_name == "equal" || op.op_name == "equal'"], + [this.List (apply2 op value x'), + op.op_name == "join" || op.op_name == "join'"], + [this.List (map2 (apply2 op) value x'), + is_list x'], + [this.List (map (apply2 op' x) value), + true] + ] ++ super.oo_binary_table op x + { + op' = oo_converse op; + + // strip the List wrapper, if any + x' + = x.value, is_List x + = x; + + apply2 op x1 x2 + = oo_binary_function op x1 x2, is_class x1 + = oo_binary'_function op x1 x2, is_class x2 + = op.fn x1 x2; + }; + + oo_unary_table op = [ + [apply value, + op.op_name == "hd" || op.op_name == "tl"], + [this.List (map apply value), + true] + ] ++ super.oo_unary_table op + { + apply x + = oo_unary_function op x, is_class x + = op.fn x; + } +} + +/* A group of things. Loop the operation over the group. + */ +Group value = class + _Object { + _check_args = [ + [value, "value", check_list] + ]; + + // methods + oo_binary_table op x = [ + // if_then_else is really a trinary operator + [map_trinary ite this x?0 x?1, + op.op_name == "if_then_else"], + [map_binary op.fn this x, + is_Group x], + [map_unary (\a op.fn a x) this, + true] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [map_unary op.fn this, + true] + ] ++ super.oo_unary_table op; + + // we can't call map_trinary directly, since it uses Group and we + // don't support mutually recursive top-level functions :-( + // copy-paste it here, keep in sync with the version in _stdenv + map_nary fn args + = fn args, groups == [] + = Group (map process [0, 1 .. shortest - 1]) + { + groups = filter is_Group args; + + shortest = foldr1 min_pair (map (len @ get_value) groups); + + process n + = NULL, any (map (is_noval n) args) + = map_nary fn (map (extract n) args) + { + extract n arg + = arg.value?n, is_Group arg + = arg; + + is_noval n arg = is_Group arg && arg.value?n == NULL; + } + } + + // need ite as a true trinary + ite a b c = if a then b else c; + + map_unary fn a = map_nary (list_1ary fn) [a]; + map_binary fn a b = map_nary (list_2ary fn) [a, b]; + map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; +} + +/* Single real number ... eg slider. + */ +Real value = class + _Object { + _check_args = [ + [value, "value", check_real] + ]; + + // methods + oo_binary_table op x = [ + [this.Real (op.fn this.value x.value), + is_Real x && + op.type == Operator_type.ARITHMETIC], + [this.Real (op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC], + [op.fn this.value x.value, + is_Real x && + op.type == Operator_type.RELATIONAL], + [op.fn this.value x, + !is_class x] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [this.Real (op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* Single bool ... eg Toggle. + */ +Bool value = class + _Object { + _check_args = [ + [value, "value", check_bool] + ]; + + // methods + oo_binary_table op x = [ + [op.fn this.value x, + op.op_name == "if_then_else"], + [this.Bool (op.fn this.value x.value), + is_Bool x], + [this.Bool (op.fn this.value x), + is_bool x] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [this.Bool (op.fn this.value), + op.type == Operator_type.ARITHMETIC || + op.type == Operator_type.RELATIONAL], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* An editable string. + */ +String caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* An editable real number. + */ +Number caption value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string] + ]; + + Real x = this.Number caption x; +} + +/* An editable expression. + */ +Expression caption expr = class + (if is_class expr then expr else _Object) { + _check_args = [ + [caption, "caption", check_string], + [expr, "expr", check_any] + ]; +} + +/* A ticking clock. + */ +Clock interval value = class + scope.Real value { + _check_args = [ + [interval, "interval", check_real] + ]; + + Real x = this.Clock interval x; +} + +/* An editable filename. + */ +Pathname caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* An editable fontname. + */ +Fontname caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* Vector type ... just a finite list of real. Handy for wrapping an + * argument to eg. im_lintra_vec. Make it behave like a single pixel image. + */ +Vector value = class + _Object { + _check_args = [ + [value, "value", check_real_list] + ]; + + bands = len value; + + // methods + oo_binary_table op x = [ + // Vector ++ Vector means bandwise join + [this.Vector (op.fn this.value x.value), + is_Vector x && + (op.op_name == "join" || op.op_name == "join'")], + [this.Vector (op.fn this.value [get_number x]), + has_number x && + (op.op_name == "join" || op.op_name == "join'")], + // Vector ? number means extract element + [op.fn this.value (get_real x), + has_real x && + (op.op_name == "subscript" || + op.op_name == "subscript'")], + // extra check for lengths equal + [this.Vector (map_binaryl op.fn this.value x.value), + is_Vector x && + len value == len x.value && + op.type == Operator_type.ARITHMETIC], + [this.Vector (map_binaryl op.fn this.value (get_real x)), + has_real x && + op.type == Operator_type.ARITHMETIC], + + // need extra length check + [this.Vector (map bool_to_real + (map_binaryl op.fn this.value x.value)), + is_Vector x && + len value == len x.value && + op.type == Operator_type.RELATIONAL], + [this.Vector (map bool_to_real + (map_binaryl op.fn this.value (get_real x))), + has_real x && + op.type == Operator_type.RELATIONAL], + [this.Vector (op.fn this.value x.value), + is_Vector x && + len value == len x.value && + op.type == Operator_type.COMPOUND_REWRAP], + [x.Image (vec op'.op_name x.value value), + is_Image x], + [vec op'.op_name x value, + is_image x], + [op.fn this.value x, + is_real x] + ] ++ super.oo_binary_table op x + { + op' = oo_converse op; + }; + + oo_unary_table op = [ + [this.Vector (map_unaryl op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [this.Vector (map bool_to_real + (map_unaryl op.fn this.value)), + op.type == Operator_type.RELATIONAL], + [this.Vector (op.fn this.value), + op.type == Operator_type.COMPOUND_REWRAP], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; + + // turn an ip bool (or a number, for Vector) into VIPSs 255/0 + bool_to_real x + = 255, is_bool x && x + = 255, is_number x && x != 0 + = 0; +} + +/* A rectangular array of real. + */ +Matrix_base value = class + _Object { + _check_args = [ + [value, "value", check_matrix] + ]; + + // calculate these from value + width = len value?0; + height = len value; + + // extract a rectanguar area + extract left top width height + = this.Matrix_base + ((map (take width) @ map (drop left) @ + take height @ drop top) value); + + // methods + oo_binary_table op x = [ + // mat multiply is special + [this.Matrix_base mul.value, + is_Matrix x && + op.op_name == "multiply"], + [this.Matrix_base mul'.value, + is_Matrix x && + op.op_name == "multiply'"], + + // mat divide is also special + [this.Matrix_base div.value, + is_Matrix x && + op.op_name == "divide"], + [this.Matrix_base div'.value, + is_Matrix x && + op.op_name == "divide'"], + + // power -1 means invert + [this.Matrix_base inv.value, + is_real x && x == -1 && + op.op_name == "power"], + [this.Matrix_base sq.value, + is_real x && x == 2 && + op.op_name == "power"], + [error "matrix **-1 and **2 only", + op.op_name == "power" || + op.op_name == "power'"], + + // matrix op vector ... treat a vector as a 1 row matrix + [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), + is_Vector x && + op.type == Operator_type.ARITHMETIC], + [this.Matrix_base (map_binaryl op.fn this.value x.value), + (is_Matrix x || is_Real x) && + op.type == Operator_type.ARITHMETIC], + + [this.Matrix_base (map_binaryl op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC], + + // compound ... don't do iteration + [this.Matrix_base (op.fn this.value x.value), + (is_Matrix x || is_Real x || is_Vector x) && + op.type == Operator_type.COMPOUND_REWRAP], + + [op.fn this.value x, + op.type == Operator_type.COMPOUND] + + ] ++ super.oo_binary_table op x + { + mul = im_matmul this x; + mul' = im_matmul x this; + div = im_matmul this (im_matinv x); + div' = im_matmul x (im_matinv this); + inv = im_matinv this; + sq = im_matmul this this; + op' = oo_converse op; + } + + oo_unary_table op = [ + [this.Matrix_base (map_unaryl op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [this.Matrix_base (op.fn this.value), + op.type == Operator_type.COMPOUND_REWRAP], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* How to display a matrix: text, sliders, toggles, or text plus scale/offset. + */ +Matrix_display = class { + text = 0; + slider = 1; + toggle = 2; + text_scale_offset = 3; + + is_display = member [text, slider, toggle, text_scale_offset]; +} + +/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add + * a display type as well to control how the widget renders. + */ +Matrix_vips value scale offset filename display = class + scope.Matrix_base value { + _check_args = [ + [scale, "scale", check_real], + [offset, "offset", check_real], + [filename, "filename", check_string], + [display, "display", check_matrix_display] + ]; + + Matrix_base x = this.Matrix_vips x scale offset filename display; +} + +/* A plain 'ol matrix which can be passed to VIPS. + */ +Matrix value = class + Matrix_vips value 1 0 "" Matrix_display.text {} + +/* Specialised constructors ... for convolutions, recombinations and + * morphologies. + */ +Matrix_con scale offset value = class + Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; + +Matrix_rec value = class + Matrix_vips value 1 0 "" Matrix_display.slider {}; + +Matrix_mor value = class + Matrix_vips value 1 0 "" Matrix_display.toggle {}; + +Matrix_file filename = (im_read_dmask @ expand @ search) filename; + +/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) + */ +Colour colour_space value = class + scope.Vector value { + _check_args = [ + [colour_space, "colour_space", check_colour_space] + ]; + _check_all = [ + [is_list_len 3 value, "len value == 3"] + ]; + + Vector x = this.Colour colour_space x; + + // make a colour-ish thing from an image + // back to Colour if we have another 3 band image + // to a vector if bands > 1 + // to a number otherwise + itoc im + = this.Colour nip_type (to_matrix im).value?0, + bands == 3 + = scope.Vector (map mean (bandsplit im)), + bands > 1 + = mean im + { + type = get_header "Type" im; + bands = get_header "Bands" im; + nip_type = Image_type.colour_spaces.lookup 1 0 type; + } + + // methods + oo_binary_table op x = [ + [itoc (op.fn + ((float) (to_image this).value) + ((float) (to_image x).value)), + // here REWRAP means go via image + op.type == Operator_type.COMPOUND_REWRAP] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [itoc (op.fn ((float) (to_image this).value)), + op.type == Operator_type.COMPOUND_REWRAP] + ] ++ super.oo_unary_table op; +} + +// a subclass with widgets for picking a space and value +Colour_picker default_colour default_value = class + Colour space.item colour.expr { + _vislevel = 3; + + space = Option_enum "Colour space" Image_type.colour_spaces default_colour; + colour = Expression "Colour value" default_value; + + Colour_edit colour_space value = + Colour_picker colour_space value; +} + +/* Base scale type. + */ +Scale caption from to value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string], + [from, "from", check_real], + [to, "to", check_real] + ]; + _check_all = [ + [from < to, "from < to"] + ]; + + Real x = this.Scale caption from to x; + + // methods + oo_binary_table op x = [ + [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) + (op.fn this.value x.value), + is_Scale x && + op.type == Operator_type.ARITHMETIC], + [this.Scale caption (op.fn this.from x) (op.fn this.to x) + (op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC] + ] ++ super.oo_binary_table op x; +} + +/* Base toggle type. + */ +Toggle caption value = class + scope.Bool value { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_bool] + ]; + + Bool x = this.Toggle caption x; +} + +/* Base option type. + */ +Option caption labels value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string], + [labels, "labels", check_string_list], + [value, "value", check_uint] + ]; +} + +/* An option whose value is a string rather than a number. + */ +Option_string caption labels item = class + Option caption labels (index (equal item) labels) { + Option_edit caption labels value + = this.Option_string caption labels (labels?value); +} + +/* Make an option from an enum. + */ +Option_enum caption enum item = class + Option_string caption enum.names item { + // corresponding thing + value_thing = enum.get_thing item; + + Option_edit caption labels value + = this.Option_enum caption enum (enum.names?value); +} + +/* A rectangle. width and height can be -ve. + */ +Rect left top width height = class + _Object { + _check_args = [ + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_real], + [height, "height", check_real] + ]; + + // derived + right = left + width; + bottom = top + height; + + oo_binary_table op x = [ + [equal x, + is_Rect x && + (op.op_name == "equal" || op.op_name == "equal'")], + [!equal x, + is_Rect x && + (op.op_name == "not_equal" || + op.op_name == "not_equal'")], + + // binops with a complex are the same as (comp op comp) + [oo_binary_function op this (Rect (re x) (im x) 0 0), + is_complex x], + + // all others are just pairwise + [this.Rect left' top' width' height', + is_Rect x && + op.type == Operator_type.ARITHMETIC], + [this.Rect left'' top'' width'' height'', + has_number x && + op.type == Operator_type.ARITHMETIC] + ] ++ super.oo_binary_table op x + { + left' = op.fn left x.left; + top' = op.fn top x.top; + width' = op.fn width x.width; + height' = op.fn height x.height; + + left'' = op.fn left x'; + top'' = op.fn top x'; + width'' = op.fn width x'; + height'' = op.fn height x'; + x' = get_number x; + } + + oo_unary_table op = [ + // arithmetic uops just map + [this.Rect left' top' width' height', + op.type == Operator_type.ARITHMETIC], + + // compound uops are just like ops on complex + // do (width, height) so thing like abs(Arrow) work as you'd expect + [op.fn (width, height), + op.type == Operator_type.COMPOUND] + ] ++ super.oo_unary_table op + { + left' = op.fn left; + top' = op.fn top; + width' = op.fn width; + height' = op.fn height; + } + + // empty? ie. contains no pixels + is_empty = width == 0 || height == 0; + + // normalised version, ie. make width/height +ve and flip the origin + nleft + = left + width, width < 0 + = left; + ntop + = top + height, height < 0 + = top; + nwidth = abs width; + nheight = abs height; + nright = nleft + nwidth; + nbottom = ntop + nheight; + + equal x = left == x.left && top == x.top && + width == x.width && height == x.height; + + // contains a point? + includes_point x y + = nleft <= x && x <= nright && ntop <= y && y <= nbottom; + + // contains a rect? just test top left and bottom right points + includes_rect r + = includes_point r.nleft r.ntop && + includes_point r.nright r.nbottom; + + // bounding box of two rects + // if either is empty, can just return the other + union r + = r, is_empty + = this, r.is_empty + = Rect left' top' width' height' + { + left' = min_pair nleft r.nleft; + top' = min_pair ntop r.ntop; + width' = max_pair nright r.nright - left'; + height' = max_pair nbottom r.nbottom - top'; + } + + // intersection of two rects ... empty rect if no intersection + intersect r + = Rect left' top' width'' height'' + { + left' = max_pair nleft r.nleft; + top' = max_pair ntop r.ntop; + width' = min_pair nright r.nright - left'; + height' = min_pair nbottom r.nbottom - top'; + width'' + = width', width > 0 + = 0; + height'' + = height', height > 0 + = 0; + } + + // expand/collapse by n pixels + margin_adjust n + = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); +} + +/* Values for Compression field in image. + */ +Image_compression = class { + NONE = 0; + NO_COMPRESSION = 0; + TCSF_COMPRESSION = 1; + JPEG_COMPRESSION = 2; + LABPACK_COMPRESSED = 3; + RGB_COMPRESSED = 4; + LUM_COMPRESSED = 5; +} + +/* Values for Coding field in image. + */ +Image_coding = class { + NONE = 0; + NOCODING = 0; + COLQUANT = 1; + LABPACK = 2; + RAD = 6; +} + +/* Values for BandFmt field in image. + */ +Image_format = class { + DPCOMPLEX = 9; + DOUBLE = 8; + COMPLEX = 7; + FLOAT = 6; + INT = 5; + UINT = 4; + SHORT = 3; + USHORT = 2; + CHAR = 1; + UCHAR = 0; + NOTSET = -1; + + maxval fmt + = [ + 255, // UCHAR + 127, // CHAR + 65535, // USHORT + 32767, // SHORT + 4294967295, // UINT + 2147483647, // INT + 255, // FLOAT + 255, // COMPLEX + 255, // DOUBLE + 255 // DPCOMPLEX + ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX + = error (_ "bad value for BandFmt"); +} + +/* A lookup table. + */ +Table value = class + _Object { + _check_args = [ + [value, "value", check_rectangular] + ]; + + /* Extract a column. + */ + column n = map (extract n) value; + + /* present col x: is there an x in column col + */ + present col x = member (column col) x; + + /* Look on column from, return matching item in column to. + */ + lookup from to x + = value?n?to, n >= 0 + = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") + { + n = index (equal x) (column from); + } +} + +/* A two column lookup table with the first column a string and the second a + * thing. Used for representing various enums. Option_enum makes a selector + * from one of these. + */ +Enum value = class + Table value { + _check_args = [ + [value, "value", check_enum] + ] + { + check_enum = [is_enum, _ "is [[char, *]]"]; + is_enum x = + is_rectangular x && + is_listof is_string (map (extract 0) x); + } + + // handy ... all the names and things as lists + names = this.column 0; + things = this.column 1; + + // is a legal name or thing + has_name x = this.present 1 x; + has_thing x = this.present 0 x; + + // map things to strings and back + get_name x = this.lookup 1 0 x; + get_thing x = this.lookup 0 1 x; +} + +/* Type field. + */ +Image_type = class { + MULTIBAND = 0; + B_W = 1; + HISTOGRAM = 10; + XYZ = 12; + LAB = 13; + CMYK = 15; + LABQ = 16; + RGB = 17; + UCS = 18; + LCH = 19; + LABS = 21; + sRGB = 22; + YXY = 23; + FOURIER = 24; + RGB16 = 25; + GREY16 = 26; + ARRAY = 27; + + /* Table to get names <-> numbers. + */ + type_names = Enum [ + $MULTIBAND => MULTIBAND, + $B_W => B_W, + $HISTOGRAM => HISTOGRAM, + $XYZ => XYZ, + $LAB => LAB, + $CMYK => CMYK, + $LABQ => LABQ, + $RGB => RGB, + $UCS => UCS, + $LCH => LCH, + $LABS => LABS, + $sRGB => sRGB, + $YXY => YXY, + $FOURIER => FOURIER, + $RGB16 => RGB16, + $GREY16 => GREY16, + $ARRAY => ARRAY + ]; + + /* Table relating nip's colour space names and VIPS's Type numbers. + * Options generated from this, so match the order to the order in the + * Colour menu. + */ + colour_spaces = Enum [ + $sRGB => sRGB, + $Lab => LAB, + $LCh => LCH, + $XYZ => XYZ, + $Yxy => YXY, + $UCS => UCS + ]; + + /* A slightly larger table ... the types of colorimetric image we can + * have. Add mono, and the S and Q forms of LAB. + */ + image_colour_spaces = Enum [ + $Mono => B_W, + $sRGB => sRGB, + $RGB16 => RGB16, + $GREY16 => GREY16, + $Lab => LAB, + $LabQ => LABQ, + $LabS => LABS, + $LCh => LCH, + $XYZ => XYZ, + $Yxy => YXY, + $UCS => UCS + ]; +} + +/* Base image type. Simple layer over vips_image. + */ +Image value = class + _Object { + _check_args = [ + [value, "value", check_image] + ]; + + // fields from VIPS header + width = get_width value; + height = get_height value; + bands = get_bands value; + format = get_format value; + bits = get_bits value; + coding = get_coding value; + type = get_type value; + xres = get_header "Xres" value; + yres = get_header "Yres" value; + xoffset = get_header "Xoffset" value; + yoffset = get_header "Yoffset" value; + filename = get_header "filename" value; + + // convenience ... the area our pixels occupy, as a rect + rect = Rect 0 0 width height; + + // operator overloading + // (op Image Vector) done in Vector class + oo_binary_table op x = [ + // handle image ++ constant here + [wrap join_result_image, + (has_real x || is_Vector x) && + (op.op_name == "join" || op.op_name == "join'")], + [wrap ite_result_image, + op.op_name == "if_then_else"], + [wrap (op.fn this.value (get_image x)), + has_image x], + [wrap (op.fn this.value (get_number x)), + has_number x], + // if it's not a class on the RHS, handle here ... just apply and + // rewrap + [wrap (op.fn this.value x), + !is_class x] + // all other cases handled by other classes + ] ++ super.oo_binary_table op x + { + // wrap the result with this + // x can be a non-image, eg. compare "Image v == []" vs. + // "Image v == 12" + wrap x + = x, op.type == Operator_type.COMPOUND || + !is_image x + = this.Image x; + + join_result_image + = value ++ new_stuff, op.op_name == "join" + = new_stuff ++ value + { + new_stuff = image_new width height new_bands + format + coding + Image_type.B_W x xoffset yoffset; + new_bands + = get_bands x, has_bands x + = 1; + } + + [then_part, else_part] = x; + + // get things about our output from inputs in this order + objects = [then_part, else_part, this]; + + // properties of our output image + target_bands = get_member_list has_bands get_bands objects; + target_type = get_member_list has_type get_type objects; + + // if one of then/else is an image, get the target format from that + // otherwise, let the non-image objects set the target + target_format + = get_member_list has_format get_format x, + has_member_list has_format x + = NULL; + + to_image x = to_image_size width height target_bands target_format x; + + [then', else'] = map to_image x; + + ite_result_image = image_set_type target_type + (if value then then' else else'); + } + + // FIXME ... yuk ... don't use operator hints, just always rewrap if + // we have an image result + // forced on us by things like abs: + // abs Vector -> real + // abs Image -> Image + // does not fit well with COMPOUND/whatever scheme + oo_unary_table op = [ + [this.Image result, + is_image result], + [result, + true] + ] ++ super.oo_unary_table op + { + result = op.fn this.value; + } +} + +/* Construct an image from a file. + */ +Image_file filename = class + Image value { + _check_args = [ + [filename, "filename", check_string] + ]; + + value = vips_image filename; +} + +Region image left top width height = class + Image value { + _check_args = [ + [image, "Image", check_Image], + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_preal], + [height, "height", check_preal] + ]; + + // a rect for our coordinates + // region.rect gets the rect for the extracted image + region_rect = Rect left top width height; + + // we need to always succeed ... value is our enclosing image if we're + // out of bounds + value + = extract_area left top width height image.value, + image.rect.includes_rect region_rect + = image.value; +} + +Area image left top width height = class + scope.Region image left top width height { + Region image left top width height + = this.Area image left top width height; +} + +Arrow image left top width height = class + scope.Rect left top width height { + _check_args = [ + [image, "Image", check_Image], + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_real], + [height, "height", check_real] + ]; + + Rect l t w h = this.Arrow image l t w h; +} + +HGuide image top = class + scope.Arrow image image.rect.left top image.width 0 { + Arrow image left top width height = this.HGuide image top; +} + +VGuide image left = class + scope.Arrow image left image.rect.top 0 image.height { + Arrow image left top width height = this.VGuide image left; +} + +Mark image left top = class + scope.Arrow image left top 0 0 { + Arrow image left top width height = this.Mark image left top; +} + +// convenience functions: ... specify position as [0 .. 1) + +Region_relative image u v w h + = Region image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +Area_relative image u v w h + = Area image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +Arrow_relative image u v w h + = Arrow image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +VGuide_relative image v + = VGuide image (image.height * v); + +HGuide_relative image u + = HGuide image (image.width * u); + +Mark_relative image u v + = Mark image + (image.width * u) + (image.height * v); + +Kernel_type = class { + NEAREST_NEIGHBOUR = 0; + LINEAR = 1; + CUBIC = 2; + LANCZOS2 = 3; + LANCZOS3 = 4; + + // Should introspect to get the list of interpolators :-( + // We can "dir" on VipsInterpolate to get a list of them, but we + // can't get i18n'd descriptions until we have more + // introspection stuff in nip2. + + /* Table to map kernel numbers to descriptive strings + */ + descriptions = [ + _ "Nearest neighbour", + _ "Linear", + _ "Cubic", + _ "Lanczos, two lobes", + _ "Lanczos, three lobes" + ]; + + /* And to vips enum nicknames. + */ + types = [ + "nearest", + "linear", + "cubic", + "lanczos2", + "lanczos3" + ]; +} + +Kernel type = class { + value = Kernel_type.types?type; +} + +Kernel_linear = Kernel Kernel_type.LINEAR; + +Kernel_picker default = class + Kernel kernel.value { + _vislevel = 2; + + kernel = Option "Kernel" Kernel_type.descriptions default; +} + +Interpolate_type = class { + NEAREST_NEIGHBOUR = 0; + BILINEAR = 1; + BICUBIC = 2; + LBB = 3; + NOHALO = 4; + VSQBS = 5; + + // Should introspect to get the list of interpolators :-( + // We can "dir" on VipsInterpolate to get a list of them, but we + // can't get i18n'd descriptions until we have more + // introspection stuff in nip2. + + /* Table to map interpol numbers to descriptive strings + */ + descriptions = [ + _ "Nearest neighbour", + _ "Bilinear", + _ "Bicubic", + _ "Upsize: reduced halo bicubic (LBB)", + _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", + _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" + ]; + + /* And to vips type names. + */ + types = [ + "VipsInterpolateNearest", + "VipsInterpolateBilinear", + "VipsInterpolateBicubic", + "VipsInterpolateLbb", + "VipsInterpolateNohalo", + "VipsInterpolateVsqbs" + ]; +} + +Interpolate type options = class { + value = vips_object_new Interpolate_type.types?type [] options; +} + +Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; + +Interpolate_picker default = class + Interpolate interp.value [] { + _vislevel = 2; + + interp = Option "Interpolation" Interpolate_type.descriptions default; +} + +Render_intent = class { + PERCEPTUAL = 0; + RELATIVE = 1; + SATURATION = 2; + ABSOLUTE = 3; + + /* Table to get names <-> numbers. + */ + names = Enum [ + _ "Perceptual" => PERCEPTUAL, + _ "Relative" => RELATIVE, + _ "Saturation" => SATURATION, + _ "Absolute" => ABSOLUTE + ]; +} + +// abstract base class for toolkit menus +Menu = class {} + +// a "----" line in a menu +Menuseparator = class Menu {} + +// abstract base class for items in menus +Menuitem label tooltip = class Menu {} + +Menupullright label tooltip = class Menuitem label tooltip {} + +Menuaction label tooltip = class Menuitem label tooltip {} + +/* Plots. + */ + +Plot_style = class { + POINT = 0; + LINE = 1; + SPLINE = 2; + BAR = 3; + + names = Enum [ + _ "Point" => POINT, + _ "Line" => LINE, + _ "Spline" => SPLINE, + _ "Bar" => BAR + ]; +} + +Plot_format = class { + YYYY = 0; + XYYY = 1; + XYXY = 2; + + names = Enum [ + _ "YYYY" => YYYY, + _ "XYYY" => XYXY, + _ "XYXY" => XYXY + ]; +} + +Plot_type = class { + /* Lots of Ys (ie. multiple line plots). + */ + YYYY = 0; + + /* First column of matrix is X position, others are Ys (ie. multiple XY + * line plots, all with the same Xes). + */ + XYYY = 1; + + /* Many independent XY plots. + */ + XYXY = 2; +} + +/* "options" is a list of ["key", value] pairs. + */ +Plot options value = class + scope.Image value { + Image value = this.Plot options value; + to_image dpi = extract_bands 0 3 + (graph_export_image (to_real dpi) this); +} + +Plot_matrix options value = class + Plot options (to_image value).value { +} + +Plot_histogram value = class + scope.Plot [] value { +} + +Plot_xy value = class + scope.Plot [$format => Plot_format.XYYY] value { +} + +/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate + * empty slots, for example. + */ +NULL = class + _Object { + oo_binary_table op x = [ + // the only operation we allow is equality .. use pointer equality, + // this lets us test a == NULL and a != NULL + [this === x, + op.type == Operator_type.RELATIONAL && + op.op_name == "equal"], + [this !== x, + op.type == Operator_type.RELATIONAL && + op.op_name == "not_equal"] + ] ++ super.oo_binary_table op x; +} diff --git a/share/nip2/compat/Makefile.am b/share/nip2/compat/Makefile.am index 54c649db..3fe0b923 100644 --- a/share/nip2/compat/Makefile.am +++ b/share/nip2/compat/Makefile.am @@ -1 +1 @@ -SUBDIRS = 7.8 7.9 7.10 7.12 7.14 7.16 7.24 7.26 7.28 7.38 7.40 8.2 8.3 8.4 +SUBDIRS = 7.8 7.9 7.10 7.12 7.14 7.16 7.24 7.26 7.28 7.38 7.40 8.2 8.3 8.4 8.5 diff --git a/share/nip2/start/Preferences.ws b/share/nip2/start/Preferences.ws index 37a24978..522c8714 100644 --- a/share/nip2/start/Preferences.ws +++ b/share/nip2/start/Preferences.ws @@ -1,5 +1,5 @@ - + From 6ef049d30e872fe44ae9731a6caf82e347c4eb47 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 15 Nov 2017 14:28:15 +0000 Subject: [PATCH 04/37] better compat handling we could produce spurious compat warnings before --- ChangeLog | 1 + configure.ac | 1 + src/workspace.c | 43 ++++++++++++++++++++++++------------------- src/workspace.h | 12 ++++++++++-- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 51c932c1..b6a71617 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ started 8.6.0 16/8/17 - add composite to alpha menu - add Image / Select / Fill - add combine mode to indexed histogram +- better compat handling started 8.5.1 22/1/17 - fix a crash bug diff --git a/configure.ac b/configure.ac index cfe129ee..679a3025 100644 --- a/configure.ac +++ b/configure.ac @@ -380,6 +380,7 @@ AC_OUTPUT([ share/nip2/compat/8.2/Makefile share/nip2/compat/8.3/Makefile share/nip2/compat/8.4/Makefile + share/nip2/compat/8.5/Makefile src/BITMAPS/Makefile src/Makefile test/Makefile diff --git a/src/workspace.c b/src/workspace.c index ba67bd1a..1847f44f 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -28,6 +28,7 @@ */ /* +#define DEBUG_VERBOSE #define DEBUG */ @@ -42,10 +43,10 @@ static GSList *workspace_needs_layout = NULL; void workspace_set_needs_layout( Workspace *ws, gboolean needs_layout ) { -#ifdef DEBUG +#ifdef DEBUG_VERBOSE printf( "workspace_set_needs_layout: %p %s %d\n", ws, NN( IOBJECT( ws )->name ), needs_layout ); -#endif /*DEBUG*/ +#endif /*DEBUG_VERBOSE*/ if( !ws->needs_layout && needs_layout && @@ -652,9 +653,9 @@ workspace_changed( iObject *iobject ) Workspace *ws; Workspacegroup *wsg; -#ifdef DEBUG +#ifdef DEBUG_VERBOSE printf( "workspace_changed: %s\n", NN( iobject->name ) ); -#endif /*DEBUG*/ +#endif /*DEBUG_VERBOSE*/ g_return_if_fail( iobject != NULL ); g_return_if_fail( IS_WORKSPACE( iobject ) ); @@ -819,8 +820,8 @@ workspace_load( Model *model, IM_FREEF( xmlFree, txt ); } - (void) get_iprop( xnode, "major", &ws->compat_major ); - (void) get_iprop( xnode, "minor", &ws->compat_minor ); + (void) get_iprop( xnode, "major", &ws->major ); + (void) get_iprop( xnode, "minor", &ws->minor ); if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); @@ -859,11 +860,9 @@ workspace_save( Model *model, xmlNode *xnode ) if( !set_sprop( xthis, "filename", FILEMODEL( wsg )->filename ) ) return( NULL ); - if( ws->compat_major ) { - if( !set_iprop( xthis, "major", ws->compat_major ) || - !set_iprop( xthis, "minor", ws->compat_minor ) ) - return( NULL ); - } + if( !set_iprop( xthis, "major", ws->major ) || + !set_iprop( xthis, "minor", ws->minor ) ) + return( NULL ); return( xthis ); } @@ -986,14 +985,8 @@ workspace_have_compat( int major, int minor, int *best_major, int *best_minor ) void workspace_get_version( Workspace *ws, int *major, int *minor ) { - if( ws->compat_major ) { - *major = ws->compat_major; - *minor = ws->compat_minor; - } - else { - *major = MAJOR_VERSION; - *minor = MINOR_VERSION; - } + *major = ws->major; + *minor = ws->minor; } gboolean @@ -1024,10 +1017,19 @@ workspace_load_compat( Workspace *ws, int major, int minor ) } path_free2( path ); +#ifdef DEBUG + printf( "workspace_load_compat: loaded %d.%d\n", + best_major, best_minor ); +#endif /*DEBUG*/ + ws->compat_major = best_major; ws->compat_minor = best_minor; } else { +#ifdef DEBUG + printf( "workspace_load_compat: no compat necessary\n" ); +#endif /*DEBUG*/ + /* No compat defs necessary for this ws. */ ws->compat_major = 0; @@ -1088,6 +1090,9 @@ workspace_init( Workspace *ws ) ws->errors = NULL; ws->mode = WORKSPACE_MODE_REGULAR; + ws->major = MAJOR_VERSION; + ws->minor = MINOR_VERSION; + ws->compat_major = 0; ws->compat_minor = 0; diff --git a/src/workspace.h b/src/workspace.h index 1a11421a..c70c1866 100644 --- a/src/workspace.h +++ b/src/workspace.h @@ -68,8 +68,16 @@ struct _Workspace { WorkspaceMode mode; /* Display mode */ gboolean locked; /* WS has been locked */ - /* Some versions (7.10 etc.) need special compat toolkits. 0 here for - * no compat toolkits loaded. + /* The nip2 version that made this workspace. + */ + int major; + int minor; + + /* We may load some compat definitions to support this workspace, if it + * was written by an older version. + * + * The version number of the compat stuff we loaded. Zero for no compat + * stuff loaded. */ int compat_major; int compat_minor; From 7bfdf00ac63aca4baffacea9ed3db89c1ddafb35 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 28 Nov 2017 10:05:01 +0000 Subject: [PATCH 05/37] remove -lfl to help win32 build we don't need flex lib since we define our own input --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index a24aca77..bf00be38 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -320,7 +320,7 @@ nipmarshal.c: nip2-model.o model.o: model.c parse.c AM_CPPFLAGS = @IP_CFLAGS@ -LDADD = @IP_CFLAGS@ @IP_LIBS@ -lfl +LDADD = @IP_CFLAGS@ @IP_LIBS@ AM_LDFLAGS = @LDFLAGS@ dist-hook: From f3ae9372dfa330ed959815ee3f71f62162fad191 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 28 Nov 2017 10:08:39 +0000 Subject: [PATCH 06/37] bump version in docs --- doc/src/nipguide.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/nipguide.tex b/doc/src/nipguide.tex index 17abcb2e..194710bb 100644 --- a/doc/src/nipguide.tex +++ b/doc/src/nipguide.tex @@ -17,7 +17,7 @@ \fancyhead[LE,RO]{\leftmark} % left-even, right-odd \fancyhead[RE,LO]{\nip{} Manual} % right-even, left-odd \fancyfoot[LE,RO]{\thepage} % left-even, right-odd -\fancyfoot[RE,LO]{March 2017} +\fancyfoot[RE,LO]{December 2017} \begin{document} @@ -28,7 +28,7 @@ \begin{center} \huge \nip{} Manual\\[0.2em] -\large Version 8.5\\ +\large Version 8.6\\ \vspace{0.5in} \large John Cupitt, From 7bc6212cb51d266f1a6fc3e05260390af4d80c99 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 28 Mar 2018 14:58:33 +0100 Subject: [PATCH 07/37] fix broken PNG Some PNG files written with some versions of libpng 1.6 fail to read on current libpng ... we had one and it broke `make check`. Fix the file by copying to an old machine, saving as tiff, copying back, and resaving as png again. See https://github.com/jcupitt/nip2/issues/72 --- .../examples/manual_balance/simp_base.png | Bin 150443 -> 278913 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/share/nip2/data/examples/manual_balance/simp_base.png b/share/nip2/data/examples/manual_balance/simp_base.png index 6430bc4b8ad4632199c4ab07668f150272dfc3de..9c7080f9a1d05d9ecfe463d90b3539234a90ae5c 100644 GIT binary patch literal 278913 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

NtJmQ`p!T7j&7H;F3(_ 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΂X8+jIbH*%CI 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�dXG%%#A}~e0GaapI;W}F}6hSPO06M#PH*A80Cd9w#7b3fS|3MWM zst$6f$Q>ipu<&L>*TCKn^%evhyC!a*Dzf%C0BvQk0dr#eE}-%vtrhYxkZ^IicJCIA zg~w}_DD8@&jfr-S=98Gjdsbog)_1_lkIPj=#Pe$Lu;6nEFMD<$oGZoNm1T(3IkWk< z|5)IC#`z5XU*Dt%OqEKBsSHBwx=wuu)tv#UTl`ae1De{GKO@UkVr{dXNG{j-RfmbwH*vGa+rO#Z7jQYu%X#~`q5m{BX0W>1c=XnC z&E5+u0WW%`L2AwI{dRSu|0;vXqrUI>Y_#?NdY6)Cnr+U|U!d_aO{8{Zbcnx>R9bU$ z8%30x#ee5x;r_D?IF_^S4CaPaVhcYawIg#6^a#s7Chor)>t zu*2C2HppWwj0??nb3v@GDe_O;4XN9B2{Q~!cZA*(Z5qXX`ooV)tI^PtOit20Rgv0B zcNdYw`!Y0t8S@OdK8NH8&0gJ9rTF*u>zB+l%BY`P7nLR6Q6L_axSupWZXbT#Rq=|y zKVadeG7wxfYPV}9Ysq;s_EE6J<0RqV>wN|1Z4%?2Bk)Dq#njxkAu(x`T*D|3IaM~h zX`gDgUq(!ms_Kwi#nw8(1UIMSU1(MR{DxQ#X{l5?XHcBr0LL?f^4FH_cN2y?Mh{Lr z?weyN{ui_4iqYCiaAEn&mquP4Wjhku3i|FIymG(SdB$fAMPb|-hykC(CLG34uDUvu zerLGw7YZ#J0rqesEU3iU;~YNMUG1h|HK5%Y@2Utfl2@tcKwomZcnhTk7k2tT1!jab zcE3*75rx=tD*@b@bUT6wC1d{Q&BDR8*&>{v$Nv@6arn?&<8BgiJ2#-~moWDk9S

GbDYh%S%NdhD)F)k_377FsaT$bX>noNW)4~O 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

0?!Z%FGZe?>7gyMlTWsIH8E3GVBE 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 literal 150443 zcmb?>)2=WKtn9LF+qP}nwr$(CzGd6CZQHhu{qK|PH#iqfnqD^3PCJ=M1vzn8C@d%d z003A?2@xd#0Kn(}4T%2=0Jl8>Y5)KP0FoktDin#=TjKw=K6CfoMTn3HAix-!G-%XP zrKnO>l4cX-uD$1Aeap;CyT)w0Zq6LnJ&o;u=(-;Vsp(H2Xg2ZYTsj-fu=qd zELDs}uFJS<%)VXAa?nMCkMo&05l_KAfa0y5n|h>6_GRE)wmfyXuEK6Rqiv%DH)dV6 z3Gmlt;tfwb23RRy_qgXfXr;yDdnn33LGv!^pq8J^KkA?)#V=D*r#5|$^cFuzyg^xT zEM*+J+{@bh!d(FbT3@qm;)B|P2!;n+S_-Ob&razcS+(6&>|(m}$OU<@z14&T43}#0yUX}L zI91I|@u?Mv`4%^S|o;*%`{UczHu5?>Q5{Mp$J*H1efnT)W?^iq5f8Tt#2XCC; z#M|c5jzH=ftBg&I{^%=I@B&0d^QP=QeZU)Uw6PCuaJby)>_?~pn@#GW1}&oC-mtmv zjDk08YFusOcA$2b<5PCxBAFQS1bt<5CTy1xe&TKPE|)SUzLc3uWExPVM-v)@%XmVO z!h6&OerRKd(?p)>?ybDm@ONfV6ao78KGn|%qrL+ROMYhT3R0xeG~wT^CTgo%QIJ*P z%a<8o@K=pX1oK#sWbuh2+iz{5Q)~Gv+Ap*i*VeZiUYyHGQ01d)I-U(th=Oc^$S0`1 z(g9<+vD_4y#P#nXHAn(v$XoGd8#%CP3KJk=yGBQdF!Y>NTI37Iw9#7+9lWx>h*l*<5&pUfJ*~XS6kygsLp5GuQEUgJc0)b zqp_C|uGX-m6%hxRc+gm!5wn7i$TnDY3n)DON`3FUg?DeC(4Kq>!=B9#hg$Sux*7&~ zgJY*299kn@aHRChnd2{IER0R_8#J|H+DlX2_4OK($NR~!OKC~Y5f0)ZOS{7`>;+H) z1#S{+4IZ9glrV5^=p5~F+lfJ($sumHS!HnaGeVZP#=Hui6}FvBJZT!vz?|v;J2^wV zK}^FIzl)@OV9-A$>W@O0_Yo`nSEyYxW z$`3k;r^r!Pn;kDM&IU19X3jYPr|Z|M0d8i)Mor7|l!$}3teJBmcTRYk*4*ivW5i}fTgIG z?$zL)eGb@k19|{q66?@PM~u)t%Hs|(TxFH)DUlOgTCVSGi8QFa5eB85Cnmf zb_fudeI5Cf{TltG{nh^Y3Gw33$S%g*p^*069$H$!9UzE_nVKPBFf)c=DDP|l(I)#e z>V`P9wF0?Y9IdPeFmqr}50r2awxWylcOhzp$v~JiaF~cQJK=-3f|A^3MFA?+=E#U$%aM*gQF8S@wptLxwrg-A zZnao#IZRQ~{UUez!P{YhO0vRTZ~Ib4i!IkCOL^?h za?u<;I@+Z166G_Q?2NHOjE=M_9u~xkQ;7KIT%#f9KDQBC=1cTD90b};&oLD+bOnk(Cm8*&ZH^^xb~Y& zI~z;erSC2yJQ`kbLtyAcB};oRq;0sRYih^1A3zf(-PS1wU{@Xf`$lV07mCCQI#H;O z0_$Kvn*$PEZqMtUym3$d+)g^}X&1n-1=iJ`m3Uq(KYAfRSYn%S4TlCRIe^lYY48=0DmP1ati_RrKjwSo z!vs9{g)6%Q^F)(2v&;&8$Y@m(=7DvoX-*9h(nv1-6?#SsrbHgQmk3M9=wH9!8f4W# zk-aE1(HhjF%L^nZ9cRMn)}$!f=ZRrQ?+31PjGsV|(UM^JvWcX>HAzUA!B1ezKqpvt zs61jlQ*?dbM+PuYSVr5w8B8$%>aq4FDaC00nl!1a7sgD z07W6xl}yKi!IZv-O`|u|7{Rzeht`M;FybV?W}o*+I|I=xH*t!M>Mvy~i84lpP-jR{ zkSCj{2`0(ea|;Nj4Jri0VFg0$SJ@%vN%E;Xzhk|CQ*6wgPDzr6f>W)(_kjgll`Vx>8gOoARPblh zr80SH8Z2qh!7M+cvehKR^ldZhyJj$`ZR9NF*?5!8*3*j4i*7zcJL7?L2RMu`<#LNutFT=G2Rfjg*} z1fcTJN=#XZLB1v%6&%0+N*j$>SKP?Q=RG@okXL=oD(Z3Qq(c!#m-aDG*XR`Skp-+X zSk~!2tu78Or|x$!$~~5(UVeHm2|W=Xp9}?&hBu(^cr6U+KXLmCRXE*;A7@b#H&o%6 zI|gHU(p%WGE=wOFoP;H{pc`tUHaTe6bUls(e5Qd2rpxVVy)fB2=(7HaG3TaY$~}q> zyopm=aI&&QymTMCibiF}X@70B%y5c+-`{{nEH7MNf>@*sd_f%3Tsj~Kb2IP>8Y~InRk&{k zy~KSf20IA5TQARY=QUAlGK1wF99cA{Lcb0_`K%S7$N)_bx;UP-Lu7<*m}%vX(p zEzVU!{!?JNplHw1kaVd4$#$B+OjG*Ufhc5z{Gp-iM1 zWD)`cIF`t?2D3-JqlhD_AwU`>?cwM+WyfVH;|hV23tu`_m;VU>TX6z1eeB$ZBVyZU zX34gxa%rEchr%+ZBFU62PlN21Y>ME(-uW_z26hjQfGY4l#7@}{jscLQ6RjV!T6gK7 z%7b&;hQN4g=~={H^h26rY5U-E<^jDYA?RDl!qM*icW}WXS*xt zmN5n7K)$g#sH`+B3QH?tB?;nKl4pW%p$&s`m?eV^v|QxjA;ciw(GLk(86%)kU`tHd zRBQJGLU{cuV1HKakqOgn&NGdF#(^_IMp*<=IHGop*8YUB-@##T-&$OhFSot?Q6q}c z-91M{u`Kc5rFZl%`nf3$D5#~YYd8!)c=op1__0CnfDi~Y!Y)cDY<`w|IHMvV`(r#o zm0}c?3mvjFIi-05r9^{{i7mGPWFw|wTN)cXcP728ZRf&TA*4?U!e@#3jN#kzbFJ;O zDF~J|^v$GpyQI!OI9omS+r%Z8o_iWBTqn0+Rp`4_w8XU{NjSG)yI32bytp&vo}3x_ zC?C*rRMjHi42gBgu*m5nM=X#*4n!+L^c32nC9|5)E?muu5_l47V1O?!3-8BrgcH@7Hho9_5ET@Kb)YWb^o^u=^C9a+pLk3c%Y zyb3!qZk9(P+j@Sw03S&pBvn64nV|;_+)YgwJmXg@qEZ)MyK#M2rpI^H9qlK4Hec(x zz{h}z^)FqKOJvFdI+^huQmnf#pdT*TT&%0LVCN9BcRG=}e?S^5AR zAA_Q?X7=pUqN}6psE{vvw^|``YL}AqehcVa?8``yg*&1L7mzj;(EI8okYgQP_XuXA z2lZHO&2*{+cikwqvgE{CJz=qX27@Sww|2d%9EipA4p7#_V~h89id>602xr0tT`%Vw z5;Ps5v|)2IV^2qskFP}wU|*BazoPP+;kp+o1~vz1$X&XJ6ILC(j|M@OiP?bS9=)7LU>)RG7z%G2Gd75j`{!24Bz6g zV0eb>_B#@}OI81&u@#Vam$)=$43#+xg4x+=he|f3BQ`cNj2MF=(RvS!8AN@f*01Q+bwWWp_ zazJ!9K(j!#saYnaH6!`?adE5eU)kcL&Nr*crHmt=te6!XTTbmDSfQA)u@x{-!P&2m z<&CZK`2Cd2Mk<;T#sXP(2||1HvDYyOY9d9mOWS25Yi6X?SDUjf*69Qa7OP^0xWso; zk*<`uVLR8}>%_K7r-8MXfhDki+mu?PYs8_Bu zgfJl(AwvNakLaGSKdeO$72~No%-HobW8ZlbiO3j=<}l zJ)5gtn1y*dUY^1$WYAzNJF?;>X)K%2`S4JPZhTWC{j`39WR?Ym0^Z$2#xu*UP;42J zZs@e+8C_mla~c|iE#101)d(zUPU&qId~#nKS%vBr99-kk^K4mDIkGr4;&dW&_`QY$ z(j=`h?W*a>>K`488gUq|r2Eb(iffYLGHn{-<6j;8?JWTl2NF2Z=GMB$j!JdEk3C^; zzHrHaLF>R|vfPCcn(G;*;Buq5rs1C)i@j6l)H@gh9M4GrruvXxRP76}t&~fn!*NpS zBz=uFcma`m*pJT86@gJX66{v-~jb(t)#sDj0yrW>^L~JRlh;QMvFb)80 z{0sj;7l1$Ki)uM}T5CuTls&bVK^t_} zLBa@Q-Gl*@Siwdbhe`tX!7M5efa8t|Q=G3YcOOkwu>_NVltsS(Eh1&A@OVd*i=hKh zw#ycyXlN^BAWu#rTsn?^l7?(}%OlEDgute_L%HN@?xJjkLn7R-0$|eE_+Q%uGx0pk zr=Hs=N^dh5>zQ?~!|>gUN+|>OC+uCme))#`)}0R@oOCBazI%21S!$M_6_T7f7}_@! z_{6MM6CC;oKS+qk7Movi1ILRlOJN-;$hNrv5=1+w4c~Vwk=HE%C4t?SeGQU2@}ftf z72(7I#+wq<){2PL94w#+Hz8(6k6J9OH6jrEI9YBB>Ek&XowoZw4;r{+O2mVGz<|jF z5Mw)8YVwguES13w3Nty^0-U4rQzO6yXktJK;G`SGp^)FN;arpI1i;)V28J=Ei?OD5 z)SA&le?ifBh+^rgy7kdP+XhX`CtWXh6xk|$!MKy)a*%TshKbL!&_;8ZBvV>y9bzS7 z*g9-7o2Hh=q=*LxYY)S} z&=}K*>{-G{$kOE zQ)HW1U`h=IojhdJLo?vR)c=qMQ?PoaZJy@U-LYuavOh90Ma3*2k)hq3Y3g;W#n|I{ z%p@V}%`2lhc{y(u+*n4fBiJohZ6vec7RrDYx+}+|yJss(StOUdBm`^TI_SgGOI08M z4RJ-F9Q2P-Gni3sV5mbV!NZn@?E@qN)@X%!1Kay1NA(xzs6I*U9dLkHOa*K|rHHz1 z1lK4eE)J{iWP=(iK$oq9fUwLqnI|kR*ydk}#W!33T&`=GkTy#h-il=soAz$38*O{&}2Hl-2-4-~3cHoO*laPB#<{QP(l3}xv zmA;Kf?@~lY6aW57AuDcPp@X+jF$8#3MjDtHXG?x-ETOC zA!j4vT(d)7F`>EQBE*iU* z3yx3aybZA5IntgOCPB-rgwa1qYbryE$~!4{Pha@ zfzidE-GBV;+h+SysMI^Eo=4<(IoK{=(Y50SbkMjEytn>4qMyOV{yo>!!V?Lw5|*=U z5JApCGmFg3gMJa^=S0+P{4`1iVSwiq6Sw&p0jdJ~KpR!^`6MX7ec40#tJ3!uR6_aM z``P1H6xxl(J8U(4VcKX*;0)ZJHC4iaYvMbd1{?^46AA!~Q8ANcwj_IQHeJB}Sx#%10&Qfm z7(hSKq;yj=>K>Jl__-s2p*+A(b5C2ME#u9He0r8SOJW3yx#aFcIp`6WZ0_xFJ>a<@ zg)ZTTTHYJYJMNWSN%aK<_2@brY~DIiHm4eMykoz(L+w#TDN~RhLR6JY4ijo3e01QC zb_zDZ`%058p7K5+jVu7#@f>E<<7>oYSLbxkKTqCk;cHX#JZ z(77^A-CiD#5C=X|5}$L`jge^h$Bh~IeucLLws!i*8YS=lqxtaXdzcf(^_n+{?zt5T zF$@;*Qxuav8PRY_{TV3fCj$&;wKG`|uB} zVQz{gza444NW~lAzED@mO7gY|Vbu4#HQ2{*vHXjeaQ*k+k45i~J>2hKY&E0R>aLVg z?V;%>PA%AF01*JpQ6Y>{PAv?WOOiz9UiJO;gV?n-4egjz9aYgoshcpYl*2U>sti>0 z=8<0K1gy0l@Y%ynO7LcUUJ|_A_b7evYzo^#`#`z0&G!xapg-E#r_;b6C3~M2tvLB> zW#5>TT=T9h&j&fjV-`?tY`}F|D&PViz_23gTS5 zsRUFVFXy%JAeO35x=OYA^OJ>fWcrBOCt5tM{8ss4~16! z&g=T~pV(^?f8*ERY@ez9D=1fZ4pRuFjsJtuDDP|};^)46=64Ll?{y1kYM}$5-=i!L z$CLyYz6%Ba3>=C4cO!UeP{q&x6K?jZ8VBV{UC5F5Ld$pf<*VWc&(*5cH|1`dk*z@h^8EftO zhSmn}efY1@1+Z!@Fp_|Ja%N^_zU&hP;S@My>P{my4vz*OpQd#SLvE7qMW{Ts3o32}GLaa2H7_ zo6vzi`75TIUR3eE55C6qOom8KZ2g7o*6}z06V;Lrf7Q|xKSt_l}e~F$gZfTJJPn7Rf6xjS^Zun zb(bFbIzINt;J>6iS~uePWliqND)hVV{cvejOO<+MuQtPQ@Qff_lQd9YQy+Zi0q`h#S6zwo)~J1xU85@u+uoFR_7=n7ok*H>fNn46j&LBO`c zeP;AgDIVDu^z&-9qb2i@Jp69saAC8Z#`#{>@>mv42V$rHt)??5J#TMjH1&U80H0LY zH9X$ICzBL3pE&UH|Lr%cRS9O+0WA8%2#C^elarh0?^yciRy15Fdc$D%WL&%-~bhc3`z>!%Q#k#kjnj-B%^)mvNQUe)6X5Afm-jo;gd zXtNSuqTHu%6~QnF#sgv zB$fN==vcM?cLzwi-VX09LKkq&{XJ8w7VK4q6pT3H9h@pg!2YcOISx#CGIus5VT~^vfCn^kc270fa*xbbDJApICgP1(rZ=ODymI zWxXAaG=qfl66w>CV!ex%tJ$OS`@eq#=g@0)`A?ZjI~D_idO9pnv>jvw zGhp>kt{(BC8Vk_*!B+-UVQtItx5w}&> z_+TSzr?4|7FH%!M*COP5SYxS5HYOQNdbfCgEnTnnz9s)*I{PZ+hug7Dw?HSHAHaf-fL|cWv=I zk;TT(rSV^m;q{L6DWr?~PaeqOt%PI;*?a+-Ry{oK5MrHSHu9%^Qp=6VfGt*6I*dag%v- z>*W7>aa(sX8lvIkl{8V;3AJL6=Q(9}$I`raVONC=QWOtQIk~I>f&5G7n{`F%y29W| z#gHr2OkbuK%YK3(0245LpX$7D|LN@#MEic>X-jfw)1-e@;)a+y zo7{rXHYywb_)l-^yHZ+eDGD1klVu77|Ctp}o2$572@#GVCCvd|LPz9AkB{!zqZv#{ z@-`wPj`I&^NzR+9W0_J4Ru2avNqf3u7%&_wx!MrEn_8C7?X1WTfxq{?0qLufhqhpv zX%AZA6Pb$%R%Qh0UZ!nIlo$b{{Xmt$kRIyl9z;$Qr0 z%iG4y#pk{J^!{4~1sK(oYfS`rlX<`X9P3i>!7SK2k1jL=qakM2SZ_Tv12<(MmaSEj zjFh9Dy`xqJ`aQ0KDFv{;wH0a_PM&s}#E5G`@m0z~{y29<#NrSSGs`%u;m^z6_kRcj zdcu4s`fB$S;>E1156Uu77j;RU(DQ%M6nf@m;=1_*?Uq4IjGyH;-NpvLP!vrtRIHaf zkE(7ec;?kh_ey#@WlgDFUTq;ExYkK?*L%L~)!kaW#W7Xn;PdZ3LxhEsF6tQre4K}j zpIit2!~7yVytSC>8DY`fzZ$obKo{j58BQ}*I~{4^>i|HWVJ(6&>gJ3bxQZv#)GoP`&Ko*np*jJd8z64Q!#`daqzpVP1=zh zILY;bfyFNT(;#>p2iXvuNaC&;5#<6+7GM*q%o??)mLfJ(l)fA4^OxlBe$NVhNtRb% zL#9xeeeR;)1YeCZf5Iya@K0X)4u*7zAQra`JuEb$nKm$l^BZ$oyYl|$mkMUjFIDXv zlEtwe0MyD192|0{x|hVQ00QXy=LV9#_Rq&BV@A0-d1=VD+foguVRqJCp8av6&E~br zZ}Tc?Ybo+#^1Ni=yl4v#8Y~r~+3feaKAsf0_Z=e2Jbe@_ZFPw9d<_L#E+2;%KP{g_ zU9`@rry~zbRS%_KdeuwGVysVTYh`KEM4W;p`B)UlBGh7^sw0Rqb7$fSw;r{mri^QMyJqq{`h_3t zn)#((^<|p;nuhplTNtfXO-n0S>tPH;bY*-R=Dl<}W#y)?ivlippzmovcX>C9AuaM& zrKZ&$T?;Kv+)DYC<#l6z)XnB!$&R;;iQ9*@>2qqHz;o`y+kD=|ECz+@(?G&++Dn4* zgw7Psw5g3$_KYNS58f}T*2+iwB&JI$r<%lFqGE+dqK~W`j>%s ztM{y9jP!8c;zxo&>NlwKX1032n+;dt(VC4-$F9HPCRU}QE2CR}>}~S`pigRr0kY@l zg4By$@rCMaK)VBbC z5u85Z@;tx!;3=~2)~ReYEQM+vfS{|a?Rvt|ul_u#ULB!ov9|Z=A|fPs3z^>RUx~Of zyjt6v7oyYpMuqnCz~Zia|EuE+0OxBO+|8d^EH8{4{%w!kX*!Eq{{mPWtF)q(Y2Njj za5Km}VF>Nuk^1p(h*>;Laaf(v+T_h3WN2PXLdUmC&#II1qh=ytj(2zS+$m0c&I$i9 zolr>%&96#rc8Kxe~6xlo+vlMxI3RPBgX+z z{1+-9GLTG_CIrmf^%RfZamIpu?e5~3{L`+BSgs$#j&aBI#Dj02Vj-1xTjPWsvh{11Ng%}qwd|O)nxcA|9gTt>U(B- zQ#TbyBZUY=l7#ENRqsI^aUQaE>lVPwVNHLQSS3k}31LWk(<=b1&q_kH5({2Q#3zzE z(a~ErJlGp}h6!qPy)Z=Oois$I$9~xdnz@JZRd??DL;C8dj+HlG5&d-Okg0gK99fW_Lz*Qm6h}jG@r%dbYn68D48)nPM_l4u_t&uZQ1;`1rV?a=9>neXQUp?O%b_j zY6l-Qsf8tfJ;hDYOwC7Y2(-tC*7i%m^ShaJCl|WRFADr=?pS@P!PD8vrw1Q5FpC<< z&)r-9(vh*v2Ec3zI)!FU7p4M(BP+St>Xo6~c36>k3LSEd4O~5G6D9+w*T!aEYTMMj^82Pw@+U*+JiX* zG8^V5aPv5>oF?1r%7ceP@#+1ec=6-*DV~zp7hPs8+NY$PtS=+q26f=4=OGkSvQ>10 z|9S(SM>stu`s|nBK{3o%N35wtjb5IEf1-@8ziOs{k1wxo5OjZxSw)9B5?CiFLC3K^#z4D(#^IBKY=MubW9MNZQZhgv-9{X z^qZOMeY38;7pdUfZ)r)7Rq(=7<4oV<6O?Cp2KhBl@z!SpyX&0{c(Ku? z2(sN&M%t*%0dr>>)E#4YHEe`$U}T;?)IjXDCTWAfD)@26slXHZbjg{mvpfV0MexL# z+J$z7lQqOQZu$GL=gI*7Y*3tu^X#{Mpk#;tqXXrio^x{}eKpL&NkM9Rj4#BmjD+!O z$&0E~<#&b-LK=`77wV943kN}#?`u2v+_y<>0`E}uwzz!t)B54kV~ttacQbe1wklcM znODNtAZOm>9^%iV+7np{1kH=HP=dO-s&7M6AJ`;OYe*J2;Dt=;vW_K=nVp2H7ew%P zCz5b>Xp9PY5K1She6x|yc`=^4DwBGey?|UEjM6DG8Qv_QW4$btW3L5wkpmeJs;50iO6D1rQwV@9>42Ct;T=fQK%wSFMo2< z&wgXN`D?P!1Bm8VQJdQ=>rCZjOeqQ^BhApe(vMn!!J%%J}%$l+y2yl7oW5E1riZSCt zshw->+tME!E^y_35y+HPgTHYqsvrFGKu=Xj@pX8oADbILheq`|{rv%ygg#KtuR+}) zGKrd;>56^YN^|k$ZTe-&#wUYBZf$j3IUBSA0-Qjjb!PPqv~o?-EdvkFexrBJdm_(nVs_m6u8$>w-)N}S-8kA z1)QHV<2EfKZ~efo=F^dC*sVbJkA9Ey?NY--KW>aW{6QDNH*8QoF~-)*fqOe>%UTG% zREx_--`{@WZ;@L9l-1ac{Y>Rna+eo?D!oUWQ~Prp77r21DS`JpVy^W2N3-}-3rn;Rib_o=|BWbGtu!-HrKCse6$^sYJigk3&kqs$mSELFz7|?}!8gA-K za+EQ`)kU__*N69GF`u+wGv!V#Cwx46iw<)GYs86a&#>{umzx85AlZ1mQp?57))Lsw z36LGP>cqfcqfX73!EHX$R*khdxJ&go?IQe?5LH^uaLkR2$KTMxfNkQBb?x&3#syF! z??fTJ5x^*qC){=?OB>kX*qM3Wsff3=5l&-he9z6JVAPf;=CwZ3M@dh*0Mu7y=3I!& zCY8E)1<^y<9W9qB;;xlpv>k&3GH<5Z7~;KE=CUtReQ47Y)1s-Mc}R91AvX7QWXc-r zw&ySgGTnT-$RkT1nXx0~AbVA8?)aMvo{MoR37{}zw0ruvkH|55vx5 z(haBsEZ| zFETij6E;|Lds%>G!%(;pl-s)ksl^Tyxr-l#sZ0VXwW?9oKlwNlO-rGzKS$5+C-#yv z`m$g;LEjz_= zAMRZnhYhTOcLjIgoTpWRPIAQR#6)vd7d&%yG`hzv-3F^cx1!la#E7yNdm0Nla(S@t zN2Xm*^H2Q^cR4iHpBgX9k+eTOZ_gPWzAs}Rs#-F?ZF|wo-qH1B7^bU|K|8kZpEoq$ zHN7_>vi0Gb*3<;8g;QM7QNV+4Frbbs?cvAv3Q|>p^7jsOPW|`CHeK5rkFgjwF4gc=1WbXe9}6H z&r_Og8`ip4e76>m=NH-Md71x|LpmHlmK0`t8l66N9+<^>lY;3~exQZ(vjw_Xt@;RbPk`}mK zh1U%)SG=GvrzWLVb`*UAa zML5}UEx2@ZFpb=sE-9WuJKBL5YA9v>c#%>136iYi5MLFcBWZhp*k-!?P>0h<;t;kw z$#WrW1ypM*8h%Fc!YkV43f90Xbdxtxh-y&r7!I$$wl4m{j&T{#Yn?J7=t&gb0cduN z2>_$c0zZR8G^+Cdh0C;qo8Epd1D>QP>6}2zf1U^9hr{DZwER5GN==cW!8=JH-5rde z;EZ?9=e&8{P9Iw^qfPTbJt~SCH=*@1795iF5qi=Hp5>hTEdo=t2U0$GHCL(;`;l8e zcG+E^nWSfm@Ko|WjM&x_Y?B6hgKQck@vG=q>GSenh_A3jpIrnpS9nAlU zZ~5M6IhlDH!Qc4~zrJSo#LLSo(~MnMwG0`E1rP|RFP`wM31D2vbt04o=GL;Ql0=UX zWy-iS#qaHo#^LUI^9t+TWBO@O^ELJQ53dV^pem=kxOZ&tX(d!QeUMI^X)x}#Mx)TG z$c4SqxPD`k;e`xjW<(%NIGZZPUdy2xk~oSlA6ybpV-G6^sCg!?r3GLpAsZx;(_C;om3B_J?O@OLHmA;K<_4w!Yf_rtyH}Q+aCY08307g3t zSr-EcZ2jORNV;shqx3cMl+R%-$a;vDPd@AQj{kF2E8drKe)%;QKErTpkeT*vWGz}W zOq5w`tJ3MC45*ky*35`G#G!P*D;w9%x~T(Sy!sK`Ps9kKbndtE=`R-%T4)!_VCfww zY7GC!qSDfubk#rAkzxDYgSl!B#wdHX4JLA}{it=+nt%9X>1a+m<<3;^5x+Biej+1p zfGhaA#hSpgJreTSDm5VL$!eTa!zd>OEJu#@50DtMx30?+0Gv*4m?u*eC&1hyej;1# z5C^IEI(i)y8F{>~7Q%ydm)`NyN^Skf+r@jrug~4x7{^0danHYerpt7OV z%+PV&X11CFMi*9v@10`^`$SlPcHm`JI*J99PbEW@4E4+F5~#j+jsbVL2MfR;;kX4z zSmlQhmstw zao9|p+;5_vdSrSMJnx0iO6Rf&&U_gNkkAf&eybvBjG9bwtw&_M^aA07tz=CW$dA1; zp&Rs@LmZl_mn(z}KFhRb?c}a|W4~+W+QSZ*@W~E={@ntd9|xGK;<=qoc1PVe+-@Dl ziQGAd29;LN!nPb`xI7VoVFXidy9V?2>tt`BuzxV1cNS|GO!oC)_D(5%7l8SXmg)X% zgaeeoq_iJ@{4a^juUo(d+=o1tm=LDep``?ku92J)4f*nd>g|@&pXG`PuPoqM{w! z_hVHQuc~iX*Zii4=G=Z3)qkSx?|!Q6PhZ$_Ot|ugO0Ur!Q~k+HOL10)K+4c1DBb_I z%%%0(Kju)pkljEFDupNwmCp>YeUrBd;wPm>9vfVqBtA=YS4GBj_mLm=m=0YrZ zD()txjH7)`<==v5fIaF0sdHnEK_ay)ggdT53phM_DP_!=IGLgt%h$OTc@=J%J@ZTvZk3zInz?7#4#P~=Pb;E*1S^FNUsBGma(km{X0@jVo66U^=Etsx_koE?n z23?j;6PGwPlDRTK7$HlHk_Yl7`kR29s`yK~iCOJsd~cIR)^BWU=Mmg-^4<32`knS& zBKpCBz_@G56_BHMowLM|$D!n8tA&Y+upMs%YMmEuT2J3>2)NO{gVGQY6{Nf-Z1Q#M zm2*4TGHY(dc?Q4$f=h%ENOCgK$|gRfXTHey=pCS4{+?d>5+Txm!@cCI!Nr2l=O~ju zBTz%ndCtiL7zHg*-hoI+$b`}ym2_QeP&>2X;gTZ3eckl;WQ@aK*^@WNVLyXqO`s_- z-I;Hg!$4jT@xhni>(@)NX7x>bCzB)QGhOqXSUmAMJoO3G12Be9}ypJd(enV{!Sc3%jUG?WRRK%)e1C(dm zxNiy^3|@6C1h}oS*LtKa+XneTr@T1;LrI4#lx;S#;@zs5o2o!QnX+Lc6n@kXnf}rH zsG@1Rnw_&7JnMX2MO1Gx;F+j>v9UC$I$*byl{Ii1o9Jl<)0YrmEblKTHX|Nuf5_6YuA9-d zd}109iuiJ?SM%Vk^z3_q)|4!kt^?5IF*2MU3FSSoM$27{_9J}cde<3S({WF<+k zL-k7e*I^O8ngNwFyeyygnLfE=twE;3sgrNz=|V1h`{ zxXS0&X%VT5Ta;NJpYhktWyN{a*um#Ejo%NZ9&dX?>9nCD@k&;W26 z*%M)5Vg(nhA~0A8fI%Yu&{+yrd_}<{_di65H>%d=@$(Og`p5WFdSbxFFFYUWKlzJK zqr`NFC5*U2oGZ)+#_q#Or1|scY&oiDT3*BkVmbI|j^rjljDk7=Sh?&*uy66hP$n|o zr$MK6GYA{({rL88X7e-ZI(2~s6k74}C$r-&J$MsZ|i3O?@JA}D@*W2IW?-x2^m+~4qhThiTDW0lPt|yHt zc&KjW(>Dyfq(FT=X#A6yhWKXbV0*OOZhT~kk&8yk<@^zpc8)^7UIm^5vQ)*z3J#u9 zVlX732EjEN+zCj;N!f~!W2_u*<~?o!bc+d!eHiokKD(R2riW9O{|T#N_}3Ke6W|zz zJ(_S$oZqJL8i7Z9k{oBS0edJ0IV^N*Gp3!;8ih86YU^lo@;rChCitQ&kP3M?gv(Fr zLG^?aj>&32Rh~n>ZF$f9^dL75vi%YJfS&em#OeoAG4|Pf4em;UCKdx>1mP<%h}mt3 z5NlEiZ#u$e6R$aXwid`S8oHRZiy@N$Zk9539ip#m^r=@qmV?jRA>3(V!}fDb$v=~j z%0azQE&}!!0ej>%FDwI5^W%i516~VVofk3MNLQGsZm3Wr@8)In=3^W`E+!OkXo#w= za-o-owTwJPGw$U5aG3IF8C9|cdiH{ryR_Jr{LAhyFA}acp%4Gp{PDA(I!}oHuTx$m z0|&x8E+=jj0+;(Hhk&g+6P#siW#aA4h4A*&F~VA2Y@?*%5OThSs5-p10D{ds`+J>- zYG+G??#2;>)3q=D)FH=D_EAmRW!;I7RFwKjnWGiC)&W!z?=q~%KTg@hn5u1ShcMj@ z_#mQNBeJ60C#SRHTF#Ahahnpxzlj6|=&KvjYK`xlEGrf+A>c6U?|~!~w34gbgH(MH z5(U9SZvDvi+17IS6o_VizpaiJa zIz;LwZJk2!COQm3j30FHql_2WHU+TgfBhK8kE(QO6UQpT0tsvN=L*9-_z7%+!aMX&8*A@BC>Rx<#%9z`_vS7y^x0G$IsX5Gz6o zGV*FLsD---n>oyWK^h;t^d;giG{^UA-|yNFFA-y6d-p`S*hiWfE$;3+?8q?}Ch6MQ zYMYFgg}ERIbvvJDrYah@JD<_w!i@qGEscc+r+{a{_}Yj=4Z|ZS33PdzmR`F^ZKIQ@ zyIQ$TUQSr=-Ac5kF!ddm-uv6}J!r1YH}}o$b+o^;@&#bpx~rA=aw9m^aN~LaZicb* zVCp{du*g_&zs|Sd9tifPwTE3djpBXeo9?1a!q`M;JxpD%PQ~r=iX~&Kt%(622B>6( z#PB<4^VK2qt%1A$sJ#D?oPCug?{De(E&Ng!Hg~=|&dfa*XhpP)pmc55Y%6{66Vgob zpYQDbZ#a>OJ5>%&mslD#Qh^@f&FMayJNUa@eSdwRKNOG!a^)P^QjJj!!}6k|T8)3i zgJo!dyRohL?r13kd)Lj%7-NAMdIFQhc|GfO(1?5_Usz22SWo&4)Dj4x1~Wqv?}_1G zc~m=LT3x1Eg-;cc&KEr5QAq_F6GCiwZ!q%5T-v(_#BE5VUQ-mT1q^~nN;m|)E8>Zh zSh>ZOOYgyh#y%Ok7UW<~2BrJ#DLk-GhgtOtBtE+s4wN1B>moX=<n zdR7))mmUh-K+-!6tiY`l0#g5C8&nd~_O-y#AYiB0aDOIdC2L@|fyx1l2u4Y+fov2M zUhaay+H*p0yqDog5$&mb4UAJyK=u!kg2B6PX7U9*6}Vb!TL zo@K}wKgq~vgnn38)=y%+RgK_pv188e*5s_(1CakRe+;Z|F|3g~0*X0ptdfgiKU>fj zC=xT_70`*^7etZWD@pq=HIOxe0FRp~$Y(?@L{d+iR2aH}NfEa2hE&hnC?Zu3SYC^noZQngb*0167nd#!GuoGlKZsLlATI8L}H^Z zT?Wml5ge#R4OOq5*EO9=FW2T9Qa$Bhn_cchM zp2}$kFWl?LyKCTlybq#&{U^uEDlUUr?IQ?3Ap^fg^t_-(%ZCabo<*i)Qv;x~Et-pc zlOulP*MQiUfbPdIeq5!4t9)nb>2y;VP@7W?G2E&j0)W|HT`cmae<>t&2C@q+om=4Q zI-1-<+z#^p2frI3Q#4RlAJAMciD(}KhU>4h(!DS6R0EXtyumKvbip~d#3Cz{F$`Ot zrtjkQXkmWP0K#BC+&G&n|IKZ=f7mA9Y0eBh2!4tb<9`2mf-4;m~Nx> z3|2>;wt9XJ0s)%|XX7fa~P(w)z=S67t1D78^c@1TQpg#b)=7sMGh_oVD?Gxf#wjF7iG zH;n!G5?{`V%uF9j<41K#J!<%U2bf3Ba=tybA>%X{KgKXT$C-A1mOZF^8Hk@;BE)x3IlTAS z!bQ;p21~y7yJfR?(8%zE-uU>?ZfsBR^T!{e_ABZ6mm8ryZ{akjR7%nGj=8ANg~-<( zz=hmMMk*kh((qJ>pmh@j2-t3y6rX?quI^>efP(yaAu}o>pA|<;7Nhc>Jf<1%61~W= z!5CAz3-fcG>41xF`*r{&>pdmK0SN`^(ec8sG1yRA*E&w^%bP}XD`c(Uq&T-xc%|xm zi1_}^DFfB;l8Hy%NRPpJlar}N1b7K8I}eAF#*hgz{3{w-MC6gH6wvh9@YVTeZx8!l z>lc^J#=6=_e{xP&5>+SZ&$bOUudLjPy3QBbX^o z%+SJc&zM_#XO_7haG96D!Iz0cNbXlO)=bulwpQv)sbyFLxuyMGhuYQbuS#JO$nV}G z8y8@vcrq>?++5o}T5ch7ZVBMBp=BFhToQhE13DS@^92Ierto24x8Sr2==IOP&{^sV z=r#yeB%n-IEbEvvTppVs#vIRw(A;{kR83|l!v2Rkv<YMnm=~*0WC~v2t>6*z}3v81HopQhCpc#v91<+gL=7!O&xm`>td$u>$ zAk+|()TDpSj&nRB`Wz`S_K6a7J8tzH4L%dW7UeL9(g~C|@@3MP<}G2|0w$f)CaAdD zrh?-X4Zu>4MLAcY^_;Vi*?mI|Xic97j!!`m5bxP?lczRVxM~?#wEb>RzgJUt^@nm- zl-bicMdjz1PhgJR)2lVaIitN!z08LNbyG zY8H<3H`xS%Kvz}%0k_CJ2gc$%dKnr zJzDzNtL5&7b*AnPUifV#=;0Q}HFha_Sb#NE%o5{x#Y${SS*QTW{K7A3(nXq0y!!*% z?kH8W#Gq1usvzPshYd#z%bvdOV0Vpn3+cqK55(3J=ja*-{#<9xm19jat8>S2s zNEhTRZAXp(co^v@E%Je_ZtJ9soPC~delHI+VJ&?jA6|60N zzY`Y|Z?V|m?Cw&F)a%=+KD&x`qcurR^C+>ghZylweS?AiI5iglnd(Ha2zb*o(`G`U zeC8h|lX{?|w9_d6BdxHKK}D(7C-3TBSfPNVGyx%i%$IJI264Fv-HD4%1a@RM7c@Ev zn%RcguH2Ny9@ds_EbqeEwn#1`XtgJTJFZ;8fJIv?KC||Tp%s`NFy1;RgJqP6gaL8D zMN^O1>U*}nP>2it@adeN_H1Jy-|)bi;AdW@y%kI$c{d@w3}v}x_f4Ld$N9#I<|(}~ zaC3v-*`ZYd-a5`|eP_l$Uw9zYidh9_@hjTtIzaGoLDG2Poa6ELLrE%0UPW#;S5r7hBJpd~_(-1?l z3e*L*uB@fLttd!^|q^ei6Q}c+=0$$*x1Dj>a zng~^Vi)qORlH+{dRF$Ll_Y*yz{VU*v{QpClx7m~Tw}<8g>@R)USfjy)&`o8%g4Yj( ziteCr#Yi^pItLV-()geIwY`L4VzV9$f4;eQ#R;-$23ikL@@fUsrdD6vg}YaQH`ne} zpfP)wI(iZ8)jw%GYet=aVLsVzk=^wlH!xcd{+wn0-dWdh4TQHUYS2XLq)04V@<711 zp&%@R8W6Kyj)?ZEw)EL}2j1<(d=Zl05V8uCdIGEicjB|RmjU@dMv$g$#h2HSX^f1; zEZiCV{(a|i1vjt%tnVmh5*c1Pz-NiGSrsrurYn=#?hUs|=??1M)B1)Z)Eu%XymdUt zx89TLAH5^fZU(Dkdlh#Vvvcp3{OP8zy)v+o&Ll->II-!MS0)y%3GaT@Fg>TRwjT<^ z_db}=8Y;I&Fg0*ub+fk$Fp6R`H~^;+s84d3Rg8@)hSSVG9lj&ceTvRA3?EC_`zqQK zs>fd?XBGY7sb(K;mlAXpLiXwuTmg~J7FS_?v^|Kz>MiNGz@?GLn-nI#5A1cZV9c)| z=PvfF+306O|Bo5)oPdt2w(wX2pwDX6+Gmi`tdT8I;5Os{!|N<99QzliS(h#(>Ez+8 zqbdxKP@{Q+R8~1fuz*t0OEICI>JVCYuxur<1yr;NjjNz=58kunUd%`qM?8^okL0pi z0jXbdwOW@u>59*Y_$dy4a^Y9LpmFN3P8>W!_$Jot97=_cNgYmn!X0z|IaO(tX{kVo zLgF+=QQ&T0p@>+h4-O}F;-0`z4rLODrd?!OO`j0EzuK*pK?qgA4F#LEvjd~et3r$I zNNl$QNQzuPXWL&ut>d!%t%VO1L^^^vSz@7M6qmA!EHN^YV|_^PHxjgtHb}Ta1Qad6 z90v;-!%(Uw&`88qW!A1#b@NwS`k=s9! z%fP2cS@J|^wA-EGS@c&cs;?|+>WG3#n8!~+#A(^+-;zKz11dQIOE}WPi8BF{ z6%EC?qy-zkxV|N+X-2F~!ZutYF0vm{ZfEMY z(wY@@GE`cM07Q+Gu<|=lIxXGG5lz+Mp;%7%6fCTms*y-v30o0b<*KC2$E{BOEa937 zPP%jzm-buU{>cHaSJDSV-ny_C`tO-7BL%2fb}zs=Ulj&h#W&pB~?v4B8bEWN**#GQ^K7TmFlQW3Szg%HHS0} zk*tW1Z)?_jon8X$-Ku+QC-@a-{aEnU+8gaM>%PnvacGsB_Z~KiIQT0OHz*?B@=5|G zyF~FxLE;e3=zP>M>_;u+bj)9bMhx$FO@AXLtL+Y3vYn-3O-|VxoOLhH2F_3M9dzWO zQ0yIF?tk$w{^pHk-C%Rr|5o(9>f9B5PX!7M&I0SU!RY0q@nG;5fbPfu;)`?zv}>58 z`6+JoLV(`PGGtF#_tir{9qHpUK)s4r$@LYlB389=tEs%f!w9dA&p)4~Kbq>;8Pw>g#h$!%eT*i5#`(+f%~V}F_gy7mK0ZwIz+Dv z-2tZ$88_ZksZlGu_j_C3980Kw&uFSkL^IETsW+^_gHUB`*=RHe1Fl%kUpzqcVkw|2 zz}_NQ$l=>m4pcPSj_?ATrf$W1-_0f*;aKIeTwA9bMs*@<5ZizIO;t>p?3|T}q9!!~ z;AuEi6D!(_$h>Q9X<)5jZ|i4cQye9nM180)3VxpNYaWW+Cqc31YMQL+`;TG#$aEU|th3dNNeu$_l||%A+`M z8C78?i(^12iy{wY%)%9C9V4M#x`shdN9V-`N9x?$`vfX80QxRiqv*9*I6duT+=IyrJPz}q*rQ=J?x|xqT2p6ZM?9`-=+9xN?d*6 ze}ai6DImSPezZyIrzB9pG7$uul6LU&XG5>*A%Ns1sAG?)8SsnH{h!Rq3nV|{BKR;j z&nq1a-8FjJuaTA2T1hhYa7(q@nVVIc7{{@o)F8_?2v?|h*v+|i{}q;*IkkqC ze6ZoH2Q9dR7Bh=XXKA`nBcG)ve#6=%v%Rog1G`yN*(JrAIz^_{h7~;%)dS2uQ5<%- zD0-~=SX?2)pw*p0Pas1qOd4~=ZUQyyVM4%s{WR|vl?to1iLgtcjV>rxt2=XWNs)Kj zgfC~L`F=OLDq;U8lACM5?biPQDw|N)t^GB~?95D?Ko$(JPK8&lS&VTTn05g8y^2$% zcR;T&nxuo{1=BN&ddaq8l@T(wZ4jqT*M|mzgw+OVZD24G#@2c!bHELV`VYPs?mYv$ zeP#@C4%8c zy=D8jeR`a^*rbW=wpGMW|9~tgkG}WD)}t*r4!TJ&egWc(nq8h~@BYaTDI}gY)?vQ> z)3EBK%UziRVCf{T@5fZkdmo_c&UK4_csbm&&DM_3j`rY_3a(*j^WG+Dl9$5>TohH- z!)g$pk1_x!(iFR2wcz#q6c{I2vSh+VdwP`}Y@IdMp{C9<+quZdd)oZha8t+uu9LA! z?ZV8kq6jv3Y;uDk(KLEh-4g7`=WKkGezL9xmBYuY`zEnMk82$D6EQ4Uwq%FsOh6FL z<}5;vJNFv+{2vcjnd66bb4+}u0oNCvS{Bv_9=WHs&f=B|8D_dqngNXB+i(0jQ?e`2 z2!KR`JM4PtSnXm~C9iX9`lXvvy+3_2D{Jfz|7pd7#sTOK3HGpGrJMd8%~s?ZRSNyo z_5JeXH8G@B-ZZ<@2xib34o**R%Lb0WM%*KFn(O6|1Ali&seTcYy0_G>*$mAesD|-nh9BNH4)|rp!t{t$P*^!+Ss9(YM>&Ls94M8K0^vU zmFycthj8H)Y56!%l4+dg1>Xrtr~ok06|0$W%n@vsp37eSAnhqYkHEzt-FN~;2*>ux zNccR*loyoH%g7t>g zn4?GgA}9UvRE#3NP%+%1hBaKW=mw#Q0tP*0_>(o%U~=hH^_yl|+-N=vs zse1qzt{KPvH9V`JQ-`;c*>3$Vh(3hbY?pnAfboN9-J{mb z#dVjyQ<43`URls&C8R%SqNPW-Bo}cV=K~o_?Bh^eZ8pi<}%l3 z#8Sf`HATsc==gaZrbrs1nxMdWvKU1tv|S}WMD?XJe)-|;e!tn=FO}6hFHiru8ojca zKNjSNRee6SgK4a5$f)2JZxPJ)V=n?w9syT=1+payMB^l5=X(?m!hGYGZkmKFW0Z(C z2VFG<(hPA|>|vSpAe1!@J(gPl03ZNKL_t)L4#Kqc|7Bkdwx3pRPs+<#ZSDLxpDyO! zYgPT)u-+%(+c~j+ofkJ5`vCd6KZ@;Tu*z&Nb$=YUCZOZt0Jc9ZzaeT+j9_G*mFC08 zIDS-n-z#jaEm=MZYFkhoRi|>Au?n~X% z9#2Qi@7%ZbBoiEbk(Z=kZBI4Vu}dqb)B13A6gUKsyvVUkNw}HW1B-w@gDIqJtQ=u7syyBE&onP%^9OUMx$>pLYi%5-NM zj(^~9>GMp>Q;0W-S%I*AK0mPBe@nAJm*R;0+gVhqyOw&)gV)$lkGm>bb{i}SdRaU< z#Hfx%^%kohdC#1^%cg)OXfX4JIYGfqL8vb5dvWT)(W?(V!)k34JxuA4Zrf^*={aJR zQRE)^+m!>p4`L*bK4|E}Ab9tGe7oBJH@^6vQOe|0 zyw0=e)&Dl*p1wrRAIz?~q-q$jwEe*YBwQs(lYgJ1>t<*oS>b#P<44ZDb2&5QnJNjK zv7U5Mfake+(JYTXCB$dF=Un~Sc}wWh4eq&s|1ExUAY+wZA2dF%*AMC_-jHHc<}Na` z52CfgBRHJE%(^0jHkqhFKax+N?NfM_N=#0bWfcs%%`{eOr9hTnDp9{%w4Y>RD-@3y z@^hqYi9uxm7$=n7SHjct3VYO|9w|?cE^5pnP=8EtPx@t7CYz7AHr4Gn`I2aI5!Vy? zn5}?m3*7ZnjAmT1iqR=$-5?jVe4ge9%}f8Z?+T2~hv~2qEKjBU5s-e`an2tjty8^r zjD|HRY|GCa2-iLsuk_isyOHqt?nbRcC_X? z>~To5P8(nl@0S|SUmv~*q(#%xx-AAgh2zh$d#pkm9E>(o?&%{v{cXGCh2R$#?@t-3 zRm&PZ7eaf$0c$k4q6*=Fgzg6ds5B{L{Ij9=<9Wdwp@Pkn+)gfn+o$ZT^XL|9AnBtA z_BZrfNm)^+*Po;ygxSU;EFhE($_A)(R23>1%`_Fc0X^!06;OX%FYALeKhJQ#ur@F~ z)g=9zY*=gL4g#lWIh#45wIf|4->scuug~UQ6z1-2SkYZ>H*V9Dpzdp5KzF@obG%); z;vPD$z7h$o)^X@YTFSZ6-~zZ3L8ne#W>``o@gYuEG4_N7tK4YVEU}>jp3qiN+1@4G zyS1Xd>hUYp52)k;XYc=*?iXv!eSno(BjJ8ILH?*$_|>K znYh#mY-D)AH6W2qOB6z6z9^Km;E9St^9swi!J4da^}#_n1n7gJVg0($KkH5qV0)>x zzSEo6-;iDH8ne;n@2MvAdmE14@zxS{ZpQ%r75Ns?Ehzh!^zZ|N7!e;kK*Li9$bwfQ zNcQ}n4_y|Mlp6(HYLxB`@@4Wvk9wIrSO3(am-O!6<>e3K>UiTUzoY;A27Y+G^#9cq zK72E~yiSs9U%9;UR}o}bC^}=5_rMS7uu#v|*51DmCZW54KWSDNGuLxwjBsWd)5hbu(x$kt{=*7XhU&RMEoeNe@ zkZ|8jX7!53Q$@v3TijJv4XK^8?8OT*9wS1zM3gsncRY$?ml^Hh^;?~p=gH;9zu%z2 z)Y9gWaCej4JcPw%T6yQtqYYs!3uF=@R^tD@kmnD@jA|XU7+FeoCq1`3l^mXugiJVZ z%T%6;c=&Qq$-yn!a-r%p0iqiqwP(bE_W@=OFQ`LIG zE>3?D>^hLmUZhn20D4w7&VlMDRiJs{A)NsKmEu&pJ1Jb#7c|7zz@#6iN2>^+VTykv zU>4Ot2_|t4;au^Z>~M8cR_wO~?T{HC9@h{kin{ss{8uP@)Ns3>6N<$A9YqMoeZOaA zO%JSLetMQ=b*LcgCDf=T6_M&WxFnWhC(N5rK9FIVEj!7WI6>oHgHKv$hlKB{wUv^- zI?UtzU?`sK%?mWIS*}?mTOZy2xg@8l?&go6&gq8^Yl6c^Y2`DDt0K@r)%-$AdVP-h zUW;woUhJ(`Y5 zdAcm8DL6NP_|kypK49HL{sUBe8zOIf=K-Wf>M$7{r+>jQJWk^sgJx0b$7g`Yud;q? zzsFX7-P8L3;U?LzP)QNSHW7A?A?9(YRPRDsQ+VuO`!DO19M(^a;1sO^k93}aTp)UMl5FsxmUnuf^K{v zO+0j$)Z(~OLM2Fc$~`Ru842J5TLOGU(k%qRe6eq{2-~W`M#V>%`Al~acPYucWcv!J z+(+*HSWPoSSq}iSd;YK!gzwZYfZNDb#~#zs0{eg4N4SjenaJ}Aw4v7@t^UBMOz*4RRgk)H%khn86u zK!t`->$B)G69zLct7Rntjc1Oqzf8EvcDy(o3zU2P=?=EFG%1XS`Z~Feth@4mUc?>5rfa{Y7yQWy6}Jp{e_j+{yjjN{ zYLZNeH3gDq_+KMX(?8XPP5sJKvhpej3h=hY=noT$s~GObFn(O6cj`IK`<=%QF+hrN z_(_yLYvGFb_zST(dfZVeQSZ6i=3w}Y>T$?eKFYwOI5|Gb&QFNpC!3T!blTD;S2Xv9 z%)@+wFnF~--KUFR&3l5q zWuKsEHFIl3-Xd2UqZ|IHZ}y$WeNK6<1gsQvA=KvSMIH@2@&em3QpdKjlO#+U6q7kf z1!2JhNSZ5%~a2UxWb_|jyV)#pb3F(ENO{JTjG0NJSKRzjql6K zj8TS180;%n?Wu61ZYBM4Gf^Km??Y)BS9V_C2^%;u`XWiYYv2(*spSDyxQDDtb?|X} zCy@-uv{I{iXy%7fBACq61f2bB=wz`n`E*7Vm)%1(@VuGD%dOf=SGh;hd;CQaO}fxh zU-`9Sx`%5{Jq+??T5&X#MT!>#V}*c;5>uTJ17MDJ&_`O<4?_k8IfM(Hyk>3~z{@1O%OB#EPEb< zKOxv#i@aIX_LnsE068y*w-1=}p&M_Fcxhqw8m_0V3*9D=ScB9VovOq<9+VyoW_fM~ z)i{eK9H(5yii=8M!vvL+be4KTP8E1>@A4q)Dt5nq`6%8JVTZz)-Bzql7JOXf;7w8a zT4uk&(T7Dh|L-+#S9(8IqqnB{#*EEdFu!l#o2`nTq@kfD;sLNTOV|1N7ILTk*ss^O zFkHs68a*SRBwL|pt@m*9;pFo7vG_d{8!$3mhL*Z4=dS%Oif?l7VYLYCm-DFfeheTr z^0TFuE!Uws;oTeL|EKM~b|gu&1TmY|=H}*RuC)lQGE$eW>Z-2pQJ9&MN$DMMCC=p!8(9w-$Xe zhSqSwv8=`XK>E*+|8$j*4DZb)FkD&I*s#XYdS(D~Yh~$e?gDf=K^%0!xi{^qi?!U% zPy3Nw1%w?I-#ygf12?!UX5O!YTM4g-$jF|y-mUm?8avup-Pw%~vM&9x;zGp>44o6% ztDw;5;&^11JQeD#gJnDLE?s;^=CtG9HrL9M!V(QUkZ zw6yQ=6^N8t+r;)E3Ea-*^7S%NOV)|QFq>BSA~oIG&gF^X4D0nIjIm-O z&31$olqKS_kP5OiT%Z&B+z*lf0D&_Yo5T3c4A+;A(@WAy?DYM-GX;zr-wBW%F#^dMyh98mSNrbunJG)4(d1Vo1*` zVhpZjIG_m74L)c@K_iLM`VxY08c%yyG4z&B`GZ0*lbtpW%mnFl#qV9ow=S^|Gvq1&m_9)m z0|s4|+Vb33OQ&zlrzn+~&29mcCDB{O!19_m_vMLj`;Ey?d}6#bU3ZxA)rjnbIcmFuNsCRnxm&SVYhCgMm1)i>-Wl&ey(%(GHB5{)mQz+jb_24taiLmE}MfGi_$0h%0asCKE<2_JBuCR4YL@!h1G$!uaY6t*}8 z-x)G#53I8aoAIzavmloq3DvR4=w3y41Vl}T3nZtc;l4H{+{>2nl4^N_)arR6HJlB{ z3a^d)aX(QFpe7K24O_TOrNNtRr0=0%77f9?0VFGHHte;Tg&KH3U{(mE(5DrUgGOFTy2G9`qCD^OjA1FbUD1cS_E3Vj{a8MzwTqPPJ)Kk z^JLUtnbR?Y86peBR6?T7mn9+P3IDj9u6>|03W}}YKDR*O#rCV&=mXGti%b;y z4TSV#Ae&5pWqUGq(hHHSbBMk$;_3m;9z zCR*Z?QZ8ZV=4#!#IeP!GyOW@HYQ*Yv9l-zr-pNR)hoXf|Zor_q1Y9>U=LpLBt)PZ|Oov zSE+m{9j$*oYH>#`Wdrti@4q;(l7Hr3HtNyQ=U)$h!S`=H?mbL5o2%{bSQy;FUCvk1 z!hY8$c*~_(*v%wVhG~P9HIN(4nw!j|ly$b@GYU5!u17ASR*P+Eo4TeFt35s{)^dg2 z=vDgj|KeiBh-@%v0{QE9;$Qx>FMN}}TkZXY^y-(7fB!}12j~~I*B=1Sn-zsWb?yd@ zR%zf^vkRf+*;%>!!|6>Tam)m+6#Ghus z#Kvay&*n6qEj~ysnU;(2bmAaw*7EWFDv{rCgnI&OZeVOF+zP1WUS^uBPyh}wnK!bE zj%Vf>v-a%4Le&fkG3HY{7tWwK5^^)Kw1x_F_GmRs({|vj^^H`#Q79keaAAY8w+;$) zQ1}w14w~@Rr!)6M4*j$l6e$+pn}tm_StOxi5w6q?;8w_Fx7Kxn!>Tj94&zK3M<|&~ zy;dXjT9v&(jmfX@HK6pWhJnmfJ`+iG1;s?W;0@AbZtMDW6`M3P2Z%%r# z@@$Gcy#kGq9KTwyldHY+7sHX|O-B|ripQSw>ZX$*FQ##hRg%$~<~umon-qxYI*6!n zI1z&}X`iiVcS0^+#jh9QAKh$EV&twnoyx`^FUiYSCja=Ga$El^=lfCEb-YJs1zDY+ zb^~=au&*@fnd@IH*V@bpFO{s`=gdcN&+o!hWdZonXyo=JTf4H?b#$Y->Vl{vWM0n$ zuHUoK3HfZAA6#Wif8Wioe=JU)L4VL&{~=?}Z=jdqOMME+zq4cO?LcT>qxynf-9-A@ zvoE`>ule&)Xom~iT&~ZeczKRmva;4E&2y{N3?a7Y`>-_C%uOVUa zfX_k8+;zVS=x;5Kj()c;pFPFhpRTk0KkdJH13Xo;UrDg`J>s0NS8G?hXuH9y06lvN zIWv56X(~qdT^!%3^9{uLVzt_b%JRgv$6s$l=GuDQfA1Fb`$bSn zM4s#JeVS7=ozJ~@kP7IE2iw>5?$&#S3%+Wadb&Cu7M`3g#{Zx$$~Tp@MY7tRh)7SDP}XkbSPa-4@FUY@l?oDABz>i z{I3JN3XCbo+xhQAX^pYjk3RfF0g2B2(M|NvnTLSnW%#zNE~o@PUILPpDWHqCl9?^_H?9Uy*u2Y}|LPs9a>hKU_j z{7}MM1^+0)kXv)04D(EWnNCyHS~UwsXzR|w7cra3Qnixd^zl+E7oXUl%zzk@*LR<` z{b;UjTq9&V+I^X6&H5&6K*n3r)3@3GglxceM(d;t3RFRDw&EfHw8cfepA9|Jz~Y3} z<4A3>mAuYvg(ndpu@RhE_Cxf_;708TaUgOS2^|kOfsUj(ce(WNI)t?;Tj)%!K($TC z)I31+%~0^0)bo)Kj?g;?tXpk$~4^=T@|@>V)E&+RE$8sRXaK!p?-Q}=*T-8M+M zpH4#Iaq1~oUG>CMt-e35VK)Za28z9_l&U7;Y(zNj>u|!TcFf3FL_`(9|{#~mDk6? z`c)}EHwvkaCb}}^tY|1;l_iEuy{w~_5>d+09L57x&sRg!#V?_#I`!>UQI;k2rHh%0 zPs|}~5ju|oLSs5YOE2VB8@0Fg{p>dnJcVndz^JBHDzEcZhO6McJx(tVtECxz99)Z7 z$CK81W@Tilb2hu|2ath-CKgTJ>Eq11?QrQvq>}}2I9&`AzRUfU!ml9Dp0kt0b&F{It8Q=bi(? zEGTr8JK*UDVmDNS9qJD*9jfrX1(TjFx7zA1?web#@6pt1A%M2;HHRk+#*17e3Mwp7 zna3K2oFOla5t1iHkdIL zd6uF_I5A}l%Vpk^Y9gLf{rjtKn8uV<9Yh+}mgwCyz%GhHD;`0uMFs**{=-*)I#!Lq zBBwwy*jTQ73aIV5Pxg9p1Nsr#ezrH!jy7JsZ0<(Vr>}nhtD9xXoNp1Uo{8}8GDcj! zz8s49niN;2Xw8DuJB|K+rz_h!usSZOm@{>`2LMRLVEKdv^>SdN-AS7o?!drQwGe)a z6IB5lx$a6U>1Vv98Xx>aInz^k+1YEKQmEpiii-wwh(S&^0c~U}i*4Pv8D&^p&!@4* zQR#V+VAej-2P8jjoC-7T_Hve4-2h$pbL%yXp6N|MHbv6JQ*SPt6?>xYB3EV2uZ(+0 z(>{muHCkWe<`8goy|e-{>Kso@7PxH#>k|$iAcpFBkmd`ph8{AD|X;l5b;!O%z=C-_7@ba)8O=1+msQ7USK2vDle@Xu~-W>U@fGwi14@d*whYn9|Q>sB^*S(kN&?-%aw5r>YTds(j9NQC|;>ARMjnEYkFoH6e7kt1vOILO}Ti_J7nh_Ny5 zT>)E^GSP^_JE6bZf?X|(Dpk%Rc{j-8JXvvEl(P14qc)UltW9!5dnX(wrJwT2wez_% zy>%>>z<^624JLv(Y7o@s>y38s9*u6*EOQe^4~S~dpV`UzlGp51up|?<3zow+qhe^k zP}Y<^(%-<5-CDFxrrAAum$$Ir7# z2`du8Lps=ibi#iK2W7iB#v_MU+* zNRMkfLu^+-J~m>k5xx~NP$={&(o?Y_K}<|@z9(Rg#o2yFj0^UlCi#BL0NSHl3 zH@X7d0zN$0>6;G{$rSG5Thrb~J zQu5IoS9zL@J2lW=aN@r|Q;eQ-bItsr7te-IdUHwWJhQD)*9S)Oc;$ZI_sJWUd*sm1 zX6Rf_;_5{AWD>qH)+b6z?bg!)lv~9G&8Ul4v@l&F!YB?Rm>wvCq*5!LvtI^6Tb;ZL zD6K8(*Od2EioT`%AH0OSpwLr)*pqa(3k-fAj+WzRkM+IDA0uNnRCvdo8!LUK03$bA zN{WIw>M2?1wz27qr6)`He8G+MaH7-r7~D+f}(N7-Q24=N2qci36&Pld7Zr`$t8&O7dl>m?s!G2saoVE||vWS>dC}aNk)p?oWjB zx0!>B+I=0rgPtFeRjLLqcgnS7!b!2IRspXUfy8Z?;4v(XTj@QL^a>PNH<3M?xjk!R zEf_(^IvI>-NJy8pRU&u5*Wf#q%H`qRtHCe#RpvdBDP)Q*ZcpT-j{!W9enlTuxAt%5 zzYgZ&-dStirndU{rbkwz4pCxYo(v0FF3I~OmAeZVxr)`m>bSp|1h^@O?I8}$xrGUr zv4w)yhbt6|Db{^^?2s~dXL0th5ULwgF_(qyl;Z!z@oav_&bFZ3E}Pu3fQPt(-2Gkt zGkUiM{?{*3X9qYy7hi&VbFt+`O5Z{k3{XDB(pWf6Tjq~?NaIkxD^FQ@@O#`#bWiCP8%#DOwxV7|8B` zmBHZVHLR}V-a-a!V?LXq{dR!6ioHx{Ubx|0N4!;(lBm5!{}yp&`yejF@rPu8M^VxR zKYxE6-)m6Ep1xW0>xIQmOJ$pw|7U*=EctxAOEE;!Nt%T?WmaO21?40=MX? zs?{*Ttu;)qyh1?HWz=YvQ@r>4cX52D>Rkim2@VxJpzpc<<>YYr96TR9{pj3kT?}{B z#khR_j~|V{S2JA=m^~WAa7F$;8vX9q`epUI*;QdQKcNCt8ZNAbQWx4A_}Gng!Du~7 z`wCz(fDbN&(p%#f>g+!D4nDrv^{7FP0~5 zO8wIU)4RHJX)X$tr$6nUk8$#)@sZjaRSC^v3RW&>j$N7%IIgvd%hk%(ZYtkct zRzI3c66hX|zRn4Mymbeti2%{7H8pR*#r}_8v--M|Sv9tQAKp>?X(yF(y3Ov>b@gUu zJsDNLOL&8|Db00ie8G| zN?~w5Z!Y8(B`RB28vb1UvM@TTof_p$ zI=!UczW{Ds_1sp_$?ZiPI}(9fQ$fsK)Z61uux;0D^}TX#TtOCehAX7PNg;lzZGa3t zj5DHG+8Sl6L}|M+Y@~1|_hk(zC8%GQiALJG+kE`XU|ejd@qICBuxBlJQ7kUkx$r%Ezrv$9Td2>4Z zcy$0G)$a26-qXrQBfK7Nl`7NvHa2GdrjREI~cz+b^G;L|K zH7TTYn8(u@V$(p8+wq#`T1??P_YU`@l(`|(0GZRqxzLCx*W7bd0+?#0v6`KYON$#M zO$@FRjs+We(XrW>!`djSVdTADb$@Kej?vR~q2=?A6 z$<(0UQPFMS%Mv1Rp=FDVQl#{Ch9Z&qyjqlijn~<#lB)TArnCr6U#K`hF7al4w>K!< zd>6<+O%B$tZT8Bsop?19IBHZhXE5{hUiRj-{CcC+H#ufx2&(ZAQ3XV`6KFCYBLy5e z;NmR;Io^of>UKMYsx6a9c{CaM!Pas@<`V%ozs{Gs#o7&0Hkd7Y@}MDUsGCcNVo$hy z{qW{wNBZMJd%SAE9V;Gtjs`!K5~+AlgTAg(W4t(hSsDTlWD5gV5GdP+kkmw0DbC4d z2)IDNHTU_uIKESOPfc9l*@0}qKCnwJ#8-EpkmBnnx5?*o_w7F2<0h8&YF~bp?&V%~ z>i1u9U;YRFuS`eq9#bW8&`#Swn-oOg7u?>FrVNKkHoWw%_s2%4Q+LgUp(LLqF4y1R!(KT-`)=xwdqW*M zgU(c8r2>z{yaCVR09fJtSf+50lHrJ(Q|HOqH@D_7p-WY}rzL$L#Qw!Kv}sqi3^2R1 z4vM}Sl4!jCHT|cvPha*QqG5txcQ*J!y!>DPm%yJ*&swv7w9;@_Gf+bUW=?ks%RuOt zLU$g>(N(~=6IWF9JX7x2AABP>lR4$Wm0&;TYY?t`D!*7QbSlHFDg6((cJs5(m3ybBmhV8Pa!y4nmR=_V@wGTL20_ZOp ziO1iC@!in*R#EEZh zeTDC*NjbRdMUKF|s(K^?#|fk9E z&eMw$G65Tk>d?7lH%Xy%-enj!PBTnaFg&cenS2oFr9DbPX4s`sc-fmkiR^Q|)lg8; zyOp&*N=<6TPO6%=(mDzKO_5KrM&moA1@Wdq`;tMzIkGs>c^|EUVs7tS=YUwmQ+R^>PX`w&OKui5<#uphPoC z%#XpeHi@Ej4~UeaX1D-&jzkx$tBEy#_0}7W{rBJe9}_gO9M5Tgd_#4!i*855tP9JZ zPBcAnokaRz$ypZQK9IK1TLDz$5@N#FK?gv{@OR<+(wo~GD z?01rU6q80D4(CHNy$FdPyNq;|7`*@oFW$JTPIxu!6x!eP7At?;t;(0ZnQYSk*%?C% zZ?OQhiD3t2yPvH2oeI84X4X+Fg{AU7nfRx7aeSwfZYJAz?Gs*)CT+iCB!h4Nm`8u3 zY(IU|dONSJIicX|F%_q%m{CO^KgFf=T}c@?ok{seeU<;8XZd5x%*38f_xVV zUzFW7pG#s{HcNvv;o{2&$fS(#OAKUWU{zkrA~{hC30}VPtEp$hV^)RO`4W!I7jCXA zOGWq+l4h^>ji(I=*8w@Y#GCG3YaH<#z8}WLFEehG^u23xxp5_cQ>I8bMo~8G2M3a z?`sE{y}Oy+7Ib`@sP00kkM>Af?A&S%5AH?&zlg~9w!2w@-bfKlC10r2Nbz>Ud>_pB z+@YSD%S_rdxRJNVj9C+M>iMXOI7yD&-9yB5{_b5#yF=l31eM#^Eo5b87uGh9p}qV1 z$u53TF30=2bx$twn;&8iH>ty81%gi$1r8`?`Q~Pl zI{;F(QmO}q+u2eGh=Bmmr?=^z$4WXEZTVGD*UsP^x6EL?!A-FH* zF)Lq<%1OsgP?I?SOG5ng*79x@JNRh5_(EH^ zc1(;dr%bXSB3`}pPS{_~CO2(s-R;Y)^R(lxV&7gc+OUbARl^&TgvH_zf3N67P(QiBB53Y{tE$_$ zyp@)fm{>WZC2adNyWXGRgX`fO zG&(yo&(7uWE6Dj_w6facNLm2x-xMZm@p=(ilZ5ycwd(n~0VxcStpuDfFGZ0;(}7$o z`PIaHE8}Q1X&CxCTq=c?ihFKp1DKSR6s`QxiU%$70ef zV*@b@aR$LoXsqey@2?vDD9{P@*kFs7c5l6r@Nrk*;g z0H=NA)i5CrNxwTyI&Uo3Q;5eG*3FEZrjV=I%z1s0aeI_d)Jz(0`nj}>GY`f(183a8~E}Lk=n2O@t=Rt ztl`PxKDmAOV}uN$0!6Y>L?QC1(0x{QmX-MnT6?bP%O%>mAY>)C=psNLCV}f{(N$OP zG*axHa(WTb@EnlD(7iK{zYlwyGzYf-%(o~P`M|&*kiS}jn+1=0HobkEvhT`M{8x|i zc0Y#y<$rB0u7Wl70QZf2F-psZTejhRg4tg)x{+)^5ap{021a7GbxrfiTG$;R3DJT$ zGVhG|I&}x|Dr~Ayq_slI+&VRL=9v4u3)j2x=AlCnyy5EZc69ymXk4v4#4mHL_YMYf z(xj=s8WLg;XeIP8h{C^E>b}CGOiq}))tgS$rHK|Z0hB6B!2C2iMkAuSb@MAG!KTVD z*w!Kge1DMX{?F4-pL}fo=7Tma9#t+;j-)sa=Vg|;N);Cob&DRMEt0EtK@mS5D>XI; z`4V3gzHclX47p8>(-Z!<&4&}RTvrW*V@SA>o4iM1_y>{jSrjgoG-W1{>ZMOU_jPF>; zxo%2BF~HFAtFcgA+6QWT2_1B5aX+NONbA8Py90i)b$QjSoX*+iFk4*vfNn{fKxdLg zct>`yX+)s<`DQvd_esr(%&^}pxA1=YdXSE2y=DMyY=LVopq>L|s1MxGoKG6E9A^!9 zj>tHsMP&ZcP~8bcB#3-}kZfW0E_vw?Yj=80uTT4_`?YUp7K}QP4=X|K)HcyBBPjji zBon!FUotFrHDRb^v4EcYma=eU=QH@nt$(`U5|L+i=Txq}3zsXue+Jx3oU0L-e)uAI ztzCVz@o!dm8!Ge{nHaS#z_sQW({v{!`oOXU1v&&09U}of-+K8;I5GxkV;@-M# z7f=*!A-JucQ5sxacdzpqCxl2(GYH=WTt7cHah~Vvk@fSTuY3F$P8k9;@5tjZIBh>a ziS6kf#m=1e4_8a+G{4lHP|i;X@2g8H`dWNs<$L~ewMhQW&GQ_5>A^#ap9}lO1R^xK zr`=$|^5Ogbs8z8k0mH+#-SBSG?o~b0ix_{vpy5vYS%Ff<%aKQIxDxkOZaU=mT_kuh z+}19Ry3+V7z_~kmR2V*Ktop0dU1HH*_9|D{;p>4MbPW8e0KIYmr2V-e54qu-lYL3e z!rIxkWpqepged+-RS(TCC7={lz`sWtQujT=AAI)zcw)z=3?@;|13i~Q$% zFg$q)k51Ak9ES6gG>U`QvnevQKXfhkrTl=2!pkb`K8AkJy%6}-$}e4o!DW>&wDU*B z_uRAckPSoQEEDJaQ9Hf0{)#C4q2l*ao6vM)PmgKCPueg4X5K9$=dHQk=ce$37vNF* zA71?riMNA+^yg{I;9tFr2BausO#3Q_M7$y`PQuk~Nh!Y5?7(-+>Hi~?sxVAG1dp0_ zR+!TJzH*%ESB>#4VO4A7>$!4X+{!ewQsey%^A~@fAU}rpTl#}66Ykz$<{L=;{hXg> zvws1NOHBIPK*tFvS!xm@ zTiUscM@^Ey3$Kbb-{f#&lV`@Y&qis}$zuBLo%n5Ke|7lDg>!F1Ij-@QPG+ms3Ag>q z>Nb=NbpG!Y>{tgj(71k#W@wpdDE@Xz!UK7;1>y%ZM`?v9mEK{8eAQf7$#vri*9s zo%|$`Pv??Zvrs*`eH#i9ir!7mAAW*D`vd#{_FBq90K2)&&sWI(3X^LSxvWiK5P%6( z{NkB*5X^69`gAn)a_h?-zr9nClh7EF5ugye8pdSVw{IUnl+{5h^a;#`s(^`vs4U9Z zgmEa&2MPt!_3eH+DfwBuZLLOdX3}@enLEML!&#D@#uavvfcq6lMbidfT#!X+iqIin z>O#LB3=d|~1H6}>|6`_Zt;obO#}NncB13u?E@I4Bw2r;S3JwA;{UomYA~s=gFAGha z6su;CFtrZZ@Xu%ZtaF>YR#8t4jqk(b$@eI%zR{!f+x9kb{)dldFE|THq%WBKW!${< ziIqadZX&6fx9sq?R}h&93&WTPW~Ftnkir4sttU>DG%6cTsRTZEOIgE&sRCjlX{NVj zSq*5I;E_e~5~(;k4wi?m^XQl2iMgd+t>Q_vVSmR*kJK9gI)#yqKiLgyS^%sRH<0)) zo(!ba+J;GlHBEQlFscy3itCj1^Ih4#ux3r~O)eaQ@+nC;lMn455$AE1dCu~RNu7Uv zl>SP!7v6aU`oE2!?rV4c3Wt_UcRWUJ{#^q4PqU(SrN9FyRA9vDr$ME^CWrr&l7F`${D@HB_DUBoa`sc~<^?nBg!{`^+rbk? zJb}`QB{aDCoH+5yogf>|$;XrNVyvqj3tw4Ia87DJBGyYaCtocU6~**5ZwyJp$}SYq zEpmlTlS5))Cy{A3V5hm$+wjw+M}-`89hH_4BmFVE{=aDC1qS^vt3ID()C)wr#Ndg) zNDp65bOf4PVX4Hje4ZRQIKb zy{WHfM>i0iIYg9gBs$9Tu(MfBG!azAkJs z9jpFP{n{L64%HsQ6!Rh!L9c6;m9v~+mN6x;>^RB_UAD=bYz%8pVM zHkXyL3&{680R|Gedy{dP!-fDPho#U0NOCXn4A14PB=qr6h<%)|=c@fw?wn|`*W*qw z-%$OVIBNt&;Lz_)*6Zd^+xgsC@Xt{pDSi%<7P7^XJAawWq+5*Y)_|Z2OiKK4C>JF} zfJ2X7?!61+JHC5=4VQ*++_uSRNn1W7J(V;3qe}YvrS}7fk)CgzwSsfsJHt^!P)QTr z>~?{xs}?t%PKgqCu9z+cql=JOj~Q=e%w)p0r%gHZon+jh*HiOEU!4w8ucu768_IK1 zADO|}ujA*r(%W4AlE^(-l{L?iCCYO7t*H*!U0~5K?VSA9{l?`y`9_^&+M=PPf-%yON2Y;5)nZ(>lGL4qqAUwb8Qk!0uB;B!-iXFmR?>~KX) zui%W<;b4gjry4&PX7Xz}sis7K;`%nBK~^wzL+*NDL4AMFr|@7^T|)EJ(58NaU-gG% zTXCna$c_^XMibUIKiyGIN@^WW)S)pHq^!M@M^(t;p(Ef8zx&abzDfr1G37xweQ$BYhL+$n^{0xWA?GOL) zf_n8eq`tw>*8m~gv@*1p=kc{|0p^P!bYN#XGeQ$uV=GrnkpGR{D5vgxL}a%cc8;Tj zy2tz?LqCw5TZ-T3$Vk z&Dc}iIeUX&-q_x2q4qWq*R+FNQZrSL49zloUCbcWo(wKzxP4hXJu4F*hmGy((MD_Q z7YN?^rAF)&yf3Sp2mW+puplT~zE!`dW6xdSKVqK@7yY-0D2)wm0Qhqn>aS3D9;Z|= z{Z3u0?-btQ!Zy`_*z&eaF~h^z>V8%%=00O85A9udyom+HjUdf{{J|CbKKv^E-$G2G zh<I2H& z1oqUnzm4U@E0Sxt^*E@up*_Eoi|P!(^%$#_1}fWz<=!d%rj{pv+o!&6#DDtbM?Wnx zn99QkC;OMR82<9g`MmRJZs#4q7d9I-xf3}?PS~G7_$83*;EgbsfOVWH*+>8O zXG1sF5u&4R8{M=RDeY3qBZ_}>nM*Fga~m(J$>7aZJy*=%giGL%;F=Jaol)~3$uyP(RD!~L$n>GW^{^iB~(ZPEaL>aB`lw! z^Lx%ooW6wjmk^$aeDM;qrJWB>9;Gvx8!UdXmp@GWoi}*B_UE{DMvNSbf&WcOkwWD4 z2sD0YohAQ&+TOEQk~}-}3-5hoMtXBI^V+&)yQ z60pz$6tpBLaXCW{XL@?NySlr~t?OO-%*f1$4DX#jL%pca@WVOJAOCaC^FZf~jzG6c z=Fkh;7aDn=^iXD{M&kq5Omd6Y{rtnORzCT~PXXpG++%$>FudKUj`AO!x7! z^4OlAWe45gmiEzqIIM^Lt@OW5Uf!}y4Uzwg6A~LXrgCtPC<)kYrZ1;Zr_u_@9zWxgc9XHT#I#(Ii zmcD^${U`cYMJHOl=an^B>>*g&GfQCgKWb_!RBLo}k(Nim@aF`fJnlh0S|I6~d@sA# zZo|xE%2>E66x_-1oZ>zR`Uh$HUzNRfHQ&RoDvtx>#jn)Eit*xKD{*&6{3I>@l>$BZ zi=^Af>3nejJih7H4EM__c65_Qip&i**|Uk=B+?Fu_Zuo3NqN|7cZBBM?P3ePIaA2R zO(j}O*yJY66OQLiD55(_)S>3xf9ogQGTgzq{oFV@lpa-1h0kK)xg1Ql0Bl(L6J3@2~>8CWACDiqxBNjWi3*UBeu~#;yPEa z#CPfP_dFDRSFc`^^yq#0B6Yfg6&db<8y@kqqk%?C!*CEp&jRNNK&pa8902fVjc}*t z)s8#cS_gO_#JdRjoaKr=CV_6Jd&HvtAcJ4_;XVLVDZUhen1TUBnX3BAP68ZeP(U4t z4et0)ul1ke>nn`Abj8R2(kK7>CAmG3_CPO)!I#_R=YsXLl;iSUv~MO~kw(-IIWZGo zWX=--ZwuzZXRKOl(7k{8XwW~;mZ(0FOl{p*ts=lzwyK5DhF1#4W1kZ)iYJ@zis3x|3sQLp{W(QY#ba_+@nTJOcx3_ zHs$OM?p&;|&J%ERgJb7$dG5kzF!e^=G2U{jec;_Ey~*z>!>=)urH2RW2pOqPC;S?y z!~Z>s$?rPP%~#_#3WE`uR~`n2*b@j@y`GeLz`unf5<)mvc&xC zm?yIPTG?7F%Kx!*bGkTxHJh4$$3x#n;rYrx3$kzZrhN_GX|i>NJl;TVJtP^=jwp6i z=VnGh;OB7VyPGfqw%41-D<89U5_7$~83AnWapXH2_EY|J4CX)V4ZcZV7{5=7C9GcU zR35N9FRSurhiQGg$@aaI{p{@h{NiNzn>&8`orL_sFa2SIzb)=2Nz;7`%on}!2thVW zU4sdHwaQpH4zTUVi|E3$?LIW_A_TSZ-Scyq3BmYXT)O+)U)(RGbTynD%(pIy{F$^! zfw#X{*h%fGEu4_3xsha;CNGRCN?Wb+;;0vVnxAl)DsXE!5nA_h6#Yb%0P)TE$rGSQ z=L#yo$Qeo~XTh_M4W&eHLESHgFY*A4{_t;V2(<Dj2a@4}*Xl#ikm0~Re)JYlo&}J~f<}oW%`GFHocAjS z=S!n3wH`(P8S9;umA}tRQ#w98GOCK(-D043ZzVbbPueJ2nEW)m>Vg}aSzZjp?>>pH zVS+JqXsde3qXU8D#LK#U*^`a3sLqbo{-!p#@}r?7E=j(A+kN(~`r;%uT^ci9T<{qP zZ=Ad=RfxZ>f_t6j_8@<8+A$tM?BQ_vtYK$$m#E_Ve^~w*YRIUq?R>}J{(9+_B7egz zVoEayA2x9Lpi2J>^fr}&2o7_YhY38Ytxwt7EAX+7KZp!o$#Z3bKF8+jwG3|Vc;b~q zr*_AjH_VOH_l(9Ctg;Cc6iMJs;Ao4s?xRL_IyJ7;;$oy;U!&2zmfg*Z(GU#WBy$~# z@gigjwgWqI>JJUKCjP)SU|b1+HeITmgSG2-_R+U+)8hw@S4N|v67!Gdqx25p%zt8t zyM&0dmJxRg8)P6%w7e1t5;UfQ;Tl>yB0n$c=G7^6cDA{CJ^yx1=gar<>o?y2H%3R< z`>}QFrE3iwE~{$*UJ|>pQ7hempvRUVif5Uo=`|KhNV$Y622wFoqzDV5h`ZPt+<=m4 zu%$B!1E6}I)6|R!IU0?DObRpHq1GNSPrng`6KmlAVH^KulKIKs|M3ovzyGoJ$8mc9 z-)(BCZBN&vs{+;qAcjab!w0w~0XH)_!eG>95lOZSUYp_7d#ILjjgObW*u_-?@DTIb zN?i`iE^3W?qtI5?QeEF@*~O+REoMEm5HF^mEhhWu&6=9NaowOaehr2vTWMkt)|}E# zQhvMI=Jz>7%v?iR4~B40;dm;nQ2}3`pj9wV^F?5993Wnni#wRPNURI3ie6gOwjdyU zG7|GF*zrcy`Wsa_T|mRtY_s(A6}g(n4`%waZ?00RH3Qb!pzRJjr&2?-yhW8nb4}_Hmrv@qQ2%EoaX>G)5LANRSDvAyNXbn@j;wQX3Wu;t4;wm*jnh!8y$>L#p)79Tw6S$jHskW*$~CrkGVa`eGLhMKg@-iX{P%y4gJ7I;=B0Mx-`4coQy@Gy6vzZvAzE# zpX8BK;xed2333x_4-lz}|Ex+pYVzgByPVv=5}z;<;Rnw#z;Ca8Sq8=W_s%{id=e5* zY-K-78V#Pnv`O6S3wBAQdyTZ+%A}_hM7Oq1iOM7stVkgP>2ROKSvJtC60XFR zK7w_iP#%DJ4eeJO6#X)n9x{J3)MgdoC%1r&i}g=_bM_iuUg?yU1SG}WZtyvB*!CGh z&iCn{$tDI3Rx=;zGFcf->g|XU@WURo-}rd>>4%z;FYjSz8UaO0MBl;SI*Go)X%$6I zPgk4sVqpEYG7O2?86?fntM`9fR_(<`zroOH{LVt|>^wWqg=iy;HH~(Mj5!Z4$|AO< zo~1rdI9a|dalw+5XFg(?i>nGd(UJ=-qp!U1%`CS3>}glseQ*_8^-?= zUY$kJ*?Zf)2&?LE1aD2=oW!K#Sl2%4#i}{U?KGJVLrT-3^;9iKTXea`(zS(~b8I3& zC5QQUk?}BH6vd__gqBDeGiKy`_aol?C)@w_I`wWX@9R~*a?{}jJxjCgB0)1soWzq{Vhw#?WKhEjzVe*P{1-zTq65NSAFd^b(0uhYsuM(fZ)Zhr)ZV?Mm{B7h_` z9RchBzX7>rVSha9@BavUeK_Mu7oo;0bl2j*A02IFmMPz}bIp+H` zHes7-IT(3e$fKd_NbubvvN>|X&@&s$)d=5E{sNLhnR_WjUZ%?X*hW)&gR}>6FH2MLc7akLN`37zwzV%Mdo) z0MNFmKcGg^SC)w1*N>v+!Sfv5b=^U}-xG0h&t^L*{&?jb__Jos?|n%DUr5=JGLsRC zX%|rI!0IY1q)YyAjcDu1*bkGLXRd5@S`hXM1C~PBSkW9nNCdH8P`!dYlrvcgt7VR$ zo%*9Z=4| zKdZM5D z8xTdp-TxG6wmJeel+M$hnJh+Y!0l0d>bL@Elq8bWD7|PO*;El7jEF1 zHYtLQ@@7s(V*|Tg!a>%$AD7CLIKNV*k*3KbyFH?0@)Y5{1vz6!`hLLm^hPBSX@kOi zmYI8;VIsv)rvM$YhITRWE0^2U9h>olnf{;`_c!^~h}cHVC%RC7DYhANw1XY|q~H=sDPXlQbaB_2`|kw%C7vIZUr z7rDdLwj;l>I+w`5)2fgYUtNUpj~jXNqxfn|Tx!wk_1v6qid!>_0mz;T$S94sG_!W1 z$%k?yy8m2l(7Lrrzc;1_S^DwjY8y z;Q@p<>Pcu&!l2A^ByJY*ozzUJ+J5wa_(|9=7loMMjt%0l4Y%lwpX zZ7!3xe=U6tH?G33tRXK1q7v=NXYbxzzKw(Y~# znEt%RR|T$@AYXi0RiC0@AAQi4)%GVV<`|EUc%4n=c3%{`N_E#D%VdPr2#EJRPNwo5 zt6XOv9|k{;cR~oi9=4zytl)90SFSt(8&7kA#luacg7aq>^`6uy)d&FT+9`!SfBjzJ}o0D9vxHE7)stVZCL;)P2|nm*Q5Z z7%xgpA8`}t;ywViZTZf|u4lA&MVQ|M!H@UJe1V2<$kB}j2ISKjeRiS#gf6be!xIQal;~d&t<#CW(Gayy3WzUS(mte9$Ld}%T zMZ76t`fc6ZB8fWc`w)&F z)E6out{lRJ2(vU?CEexFjbLK6L{Ovv0CD?`UZ&ip`ZfTtdg21HFKo7euqO@nAJQqe zI>pL#z4<&(2&FGzB&h21;c&6ox&=wGo%$Z1al9nZJb^H^fk0L$T4@jnld-a|^Jyff zKyR|qgpTJMxZ=GrFg9G}To{GPX1pZ6UKWMq8!HHr8feH6@UDWQe1>{H z2<_Kek_=$=PxeTR^kQ@AP8~OXkkvj1T7!ZGK7p9e45F>PJjQ9Mw)e7~r6gW2ly4*<^j>zuj53o#Z=SAakr@mRk$^HL_i@z)N%*e zF52wHpx2b^C`&>-flw?2&kYDdHwlun#D`$~Fip=85mf|2a7ZR=)SE3C#psqcE%z~ngQxa zPvbs{&jT<<_!}S7Z6r@ehKDFxrlx5`S(r3WfCAx2xj&Tu?7MYas#C`Q)K5{e3dvb= z{kvvS1Ij-DCUmc0=X>6_2)a_ebytzBH!dvjcRZ|CEgy9&zxaNJ)e^l-8nTRb5?~Lw z5k)iWI4V;?aw*&nT30ax9TX2V0QMh?ne3mVRZ$^Bww9W}B9@xDkUk#t8+z=bp{Khl<4!^#M9oVjS~&P*`0W7t?yo)J7ID`Q_6;#<%h*B~+n_b^ zp}D#YvO2(I?j6Zqo&AbAOQm2Q=W$^>Q)s~Q;)=!ZQ>^270v{AK91%?JW?+P-VOa6Z zVjWy4@YVvakwa}J7dL}QJNKtYfE$H+o}9DKZ_jIjdsHpfTI2Pwb^ZH%b1nmUGAoLW zuE6ipzOSP|nn%V0Pl?CgI;&XQgw|6R*4&qiHaeiRG+-WS^D5W5jMD|n>rhf!n;97H zM>FO>7G!DV)sE?B_6q(2DyhxxrjHd~q{WlbjoTi6U4 z^pa_$sYtRJp0go(`_S0d+E^z-)4l83p|_=j^Lh>RT0aNdg50$uaaYhV#>WVj45A^z z=A}Gw0}3L5yeQZRbj?yFRcNPrC?Pv^P+^g}1QJiktU?sK;w?%0mBC%0ST3KA8RG(r zZ<2;9z{F0`o2D)EhA+8^P;242`{ds17aJ_P@_}U4Hc0vry?`cvYZxJ7uz6DP)m(L9P%gPZ)FEouhQNKcpTOPK+waRBx zteiqPk_#U%YxNnpj^rY5t&@jjcvxQ#4mS7$5dX4`4oHX-&ZVcNiAC)*lvx*-$xBS% z{Z#AiB0H$^sASzJiJUgUyr1Q}sZFz$GBX3(!0Fc8IEk%-j=DtR^C;RP_-F&q!Ejn+ z-a3r5q?-YNi6A&9n+edvm}g`27ij$A9Npbtpe?ZcOp^Cy#!Djn8IVVS+y&CMycw2h zuwOOg23e*Nkqk_(oT6nAs-kI&HKle*tU58zp9HRcW7*EcR8`XWm-G7h0{(HF4}P+6(ZpGOkDq_D{CJjPztD9*(y+2Nt=U5Y zx}d$~2AR6>g!1pR8hXEihvD!Ci~pmVHU8;dT-w=nKwW(eAa7q=>$heC_FgpM7b)^N%cIXI9Ua{<`~_Nhmk1{SyS|I7 z7i+;X*hzp5*}V{+J2R?w7n8t=?5Z(2TEN3OIC02JupO>ljE2V(2XrqX_DldvK(xP; z&%HEvvdNM}Z|}U-MYLW`i;YToTOR$5&6rlzoVjkR|47PP<@Ld15fk^#EIZ}uKT>;+82Ynuz6 zKfcNeFoGmh>>9-mHu#qfHeVvuPs`J*{54!2T&eb@h>E2huH@E z4sn2u9zAFH_C_XWXzA8J6zr#M2xmpAN*51iK_$t^j^XsQI6f@TGm+=m$p{OUc`|eH z;jok}d29-c>qW!baz=J&%tX^c84JtSp0J{%aSR+2GWe6#4a)<-s_!FAe-Zs!hFZ|2 zpwO#_kUjW3>g^LXK3J7~x6Z1~W*UPGPGd=%cS#_X+n#sGq{Tz|o`zc$Y^HrO-jFxj zMf;y#Q_LH+u7Tb2aqs57Y31$s95P(C@I!^2Rr;Q6U9gzM>RSuv{evRvPvsM2#<0utnrpSP zlYVWjS1;+7ytYJ}6gW#;MF5g*pcP2i#!}T9AA<42D*fkTxZMegPQv#>v$4AdI}3Xs zfhk@+V1KOjUmJ|y zmL-A$#KkLQ*iY3?bmJ(t z1w$NW$B(7;4mODop<9pysSFC6KG}k(Qf|pi-f8-T2Ov13SDFaP_+ZCBiYn5I0^QUO z-7D3X(>p;>QSssHdIx7}lk(4;E(N*A)AX?%XU{f}B4j&C);@IAk_UAXyb+o8VvCBZ zs{loAWsUQX2A#LfKv}POZzM53zNZThaJ*H1D&@>^D?#@g+NUw4Jn<{a!g&b+bryff zOpXqw_@~WgtHENaTjxPe404Io6BeYKeLx&+g)DY$aLXfiE{o_`zBb^w>Ml}inpd_f zvT%HQqQ;KF*kjY&>UCh=92=#=sY*lkxsWNfK{o}zBn7%pAl;hQf7;{@OzK58A(4VY zygIHYb*a(GX{MCGPpRFErgl&p7V>)OFsL3(%1^G~%wEy-nL-_oyx1I`r zV#4H_%&*OLa%!k9Xs=yu>{g5^rO#R3W!NQkz!8+x;3BB~@kXoVSmjEiP=MA+qzd_K zurVIrzstyz39^cJR@yule}~#P?Bu7&y}An)?=@_l@BL)AiQZLgUoK z)+Wmj$KIgY79hHhK@~Es(OLyTMH-``l-&!^lH=fI8#+i+JSb38bC-*Q`t-JsoDkse z>%~t9H(rb{3UYLgIX9*B{w6^Fu*rwyPxrn(z44c~nPZ0O&8ArDu(fs8TaQ?p(6~sP zS6g{&ak23h-W6Xy37Y+&A~M=u?MCS^jL4?hoO!%u+S|3z(sCL`r5OThJ}~zREVwDh zcD784@GV2DtF;T0p8-52}cU4aKezg;fMaOYV<{JEjQO-jH$OO7|@D8;sG;NvYu4un&_ozG+SE7mR}F$ZvP(=Q#({TWJK|3wV23b0+Wq?1YV_NBUaaw7 zj*EKvj~lPW%FUTqilArlyGELHP3l?x`@Ka?72`g@|5zhL2#Yvb4@woUI$a~Q39V2# zcM*9gmR)c&a0$}FJ1CIKqp)vzyCJl#<&5i#xV$#}-U;|a=||98R}iBD-QG*hAOBDt zH=6b{6)x}mW*zKAZ1cTPp3{Av-6?Z2&OM6Iuu?!RsK9xcp>Y?Cu=>zp#31_+j2{Ml z4O(UpI@8#LIk5mBxP2iVOGKS|P3(-d7YEEE^k;y@KL1mr)|6L-4L`qwp(1EKTqjSd zJAxLlt?Pw?B!PYXCwFLYyG$2v3j*8W86vK-Q`{E zy#`Q%eDhk&{-}Y)HF{4-X$~ME0iiprC!&O)sA9|Lwul|bCF;a$iL?aP5=)>BM*v>U zbw-(KtPJ3Ahh9iLuUInv5Nluj=QUyR$Ul5qZ@jr;xl=Fwm%Y)ba~=L`d=B4{pNFOa z>s!VCy_&B1^7MUKTViVm%Un;&2ms|VxYT14KG54A54y${skeftEKQ6A9$Bf8Exx_P z!7BTcZZ&2mkQ8srdfgP&TR-GUCRvoOo`96dXaliUb)1?r7g_nRjUH; z-jL!EQObU~t90aI3djrZWxlj_K$d_BS*QTz#M%FR)wzqaWErE%$}S=k&fg`3ZxYJ{ z7!RX@8zp_r3sbd(-q!F7pm2wx2@uZ33)9ktA~r^$WRt~Wp)`TTBj(kweALL0X^)7h zBRy@1f0fjb%|Pk|`6GHZ+o{qQ5%^dk5*TdfAQGa7_rNY_3r@ixWa4{z0rL6=dQJA>3zcoKdvWjlh#N;u-K$=he2qvXo7cBd3`E~zE3_DHO;bm9qCn73jz*~EAX zl#?h5x~298;6QZL`QpD$>eIqg-Fq`V-+OQmLAOp!;t%Fp2(cNRvL`2%t>ejcI=-5{ z$M8VA$Y>@aa!yCe5w=GliOY6!vGMzZ?=rO>FwN=<};8j=fmrI3%VyEM+>FY z^ML{s>3N>AGUELSuD)L@cxih{toniBM>J`6X1YMKc z2swG}Ngyh0S9I#-I2#gmr_2`&+xaj}|De}g2 z#7(*!(IfT8(pOSHTE zZjojBC1PAB+1Ai&qs?X|x^sXTZPIL*sk0Kk+=AZ9n#1zO5s@G+-TN?=-&XUHb$(gC zd%FHNy(YD@@tXUq`j=bEy{dgha1Gc$QbrppSnb0;v(52J9$KdyKGrl zJsS}_Ij~oYx{XYE`5ym;)gPEJ)Xu$<50Nd{QP6x)2I}HQ$APt9HKw2p<$%ZagS{`^6D2R-GJr7N zN2{1ih~suH4t9S2kOZ4GZue38Vpr;u1oRA5z@T&-nGe00(i-OkhWBR$>-fz|NW%cy z6GTY1pzavyZ?JxV*Li@fnH6q93<9bvB4pcx+DzQsC5@KA_9}53Rw=ufhkNQd%ab-5 z%FnK|Phx%0ipfE()FN57R4yGVd&fexW_BKynEir!5XN0NY?c=}4RWFeRV-Kjc+gAO zJnV57Kva*?DF<%yAsF z`18T*JV!yVo;xz3o6V{XaRCGQ7D5yelu^`LC42`)^^{)Ce9ORBnq`4?Xo1@*k3^+#Rq6ZT zF$o|5Qi*f?F$^`6(Caf>N&r(_O|uRPdk~vlzva9)OzAwzHs7Y(YuH)r0vEaMXzLAh zi>Rahc>?>YkW84xRH5C7k~*bQxm!w(Fdc!kW+RS~<~`Z;xsm}M7-7A*Xdz}x*Q!k) zpiz)@LqrBB8INFT@V?WZlqgsrRI3h~a4#pAcQ&ODQBHMe^Ka&h5jI@3oZrSph?y5= ztY2D{Y)jp#d%+gB)*^3GWx6iZOD&K+5RK}``;jYQ-lh`Fq!_1gi-bq5?YnV%c;Ai} zd0`m1JwM-{Uv~3tGRlt8vs%m`#+>y3_+>nM6J4L0H{YYs})}8D2HPMXp$@R)UGIPpZwlkexMeuMey-1^6O!CoCpPc4_g% zRo(rel<91!{svNR=KD)4bQXu&Z6&g3eZlxD!q3Ku=JWnf?9S~{1cq62;z`A;t$dqB z`{^Sr=u6-7Ut#y9C-YT3qYkQOCtL3q_mN_%ha1AZdiNlLxndlahS!9*&cYPNT{hp8 zle#tZa6}$rmONYGBVDAyFJGW6byX z1ZNlmqFmB7X@3>*J1rxP8Oua{r zqgBrU4=Jz&%&Be+mTbQ*+Q!3Dg0z9415JgN6I9AU$6Ox0x(}L*S0!W;wu~93ZxY*T zhr=d8G_yKUiApbfmjCQEwh!f9|1uW17M?cXqya4zVdk~WumfeCV_j+`W(6h^sS*o| z=+I&I@9|@Zps+dBgt#67@yu3f=L{ofL@&|nRcNu(&`Rg&czr^1uSRWk|~7H=^{IaqL&Or}Xx1RCf)U6~pTtXJe%`goPzftNER2-4*y z3rrDLO+i^c@}wGXMYQD{Hs3d(i?tC{TNh4qVIBBWfOnnxi~_cysINGC5ydK8*aZ@s z^PF`63?Ohgu8RtJL0pPB~;Y&NYg0lj_v3~ z1fT^{;N)IyTeFv`;>UbWZ0ENG?rp)%mX3|u@QqbmF9|zdS?@LQ67^S$b-FRyy+62_!JCO( zcGlztdFvM3w|)L=7xzs}wLx?NFsltE**2ffQt^5d3Yf^iJdLn298@K+rQlMpP3*II zO$sOqfSW9_JBJ5l^l|rX^{Wz&geqIS7UDmGRU2LHoul3Vhj8AQ+V5ch#uBm7qWAr@ z-BdjUmfnR5I)mcWQej;B(=A(73EtlI?!(l^-IF z=L7mtIe*@$AfIZ{ZjEJBuiX#Zz_9r=atTv!SsOGQ=bUCG!VF#pYj=BRn%TmSa@r2+ z7fa4|i*QYZTa6sU;FiMHR-9Qv?&kiMDXdj9Wws=KH{ZWrh6;Jqn{*|o(Sq!HKYpM< z)dwwdUye!xt08%kTw!Z!)@+faJga>Mtwmq!05deY$CZs95z&7SAL&+PQ+?&YJ(SbAJF_e1cp zMKtPMqR2kgCa_w4%N;p@AqVhfBWA0SAH zg*#MTXm;l9nv-{Nth6_;HYL1w8X)GzB-U5>{2OdI;?i**#SVu60<+!eD2=QTc9p9- zixe*dW8csPNb6Qj@&BjoK9(fOvNSO(=cdF|jdOF(gGXjnWo7ZMnXX0;W|{~V*kFMt zzNZtlXaMR-yhjFso$!d^X5e zgOtP(xm+HG(CzoC^!K`)nMupL0uA>F@V4U1mmR1Hj4}%G^J5I)b2q=pyq>nt493b6 zPU6eAyZYcmk(+bLGbTo-+8yI-k>U1cJMe1SS78o_?S$Z)7D9INY0Ob#bHS@!W{yG1 zqGsrIM+-|6-_+xCM7dchH+{$E`fnxOc#9_IjX-r*qxHZN0QI95mo@=5UAQ{Gacu>|pZu9kUa#ORXQ`DK4G-0fp@<4X%^ml7@zl+I zpT&ANL(U9=@k*QF(boke>?W`6lx9!=$$7RJo41JyL;!5e6Pi|HHw#$_iee_Y{=Ftl zmk~i>@{&l|)4*Z?I339mkWQBIqMTCT$R6oWupXm0hvbZ(T~oWio`AY5Kx;D>(Z;!V z0Q909F9w^=G9UDM+H_dxC#l7is*SVBLJ5{_G4gNN8JGo}RhSe2n@XUZPP`Z6`=M{L zRt_@UB-R_DELLCKaz0v3yXz?Tn&v`FbNi}jx+Ws6CXmp9kep0DI5ylTU@Kxe z*^Wk31Cm3A^ zQFd!z=66zRCdZLu-nG&qN zdeBKhZvDpQ6(xJJtoqN0pkwV|sWm;l1|NvEg1qJeg}+v5XdzNL*pQgw1$#ht{WYZ| zh2{|3_HAWsG`kHxLOfAjWkn3 z*4FYd@uQH-C*H$KklI?a)*#`CcS;<<<7Qq0@=XX|V^~gPnR0C<)M7p#6LcZ%G&wT; zS)_(;I~ktVmP0DLm;o+X!*4yO`P2ipDkJwGmA|d9)pCih$za*UvH(;l(fJHc=7C$G zhy0*)rqb#hwy7 zce5PSHv<3cTDiIF&tY}uEuTrtF@~O-vaIr1YsGT0+$j_tl2}c1lNMvFT8p$_Se=Dn zYo$*^eG((nB?fsIrUk&rkbz*FQdD$K$Bs|;4b7}rmDwCM`8V&lSKq+FFuG~+tG;4P z@$}19c?4e=_W85Sbd9 zt#s8I5W#JN_huLnA&Tk3la=dFZEg`I!;2hzsnNJowg3Pi07*naR0Sg*1s>hGRQS|nM^j)$x zgNk%z9rR)GuDF*Jz!RQs`y7NSiHkF|I-oskqNVW@SBlLtpanBS2UQ&DF2XjQ9jZ{U zB!jah<`3gw7Wi`qo%hM`l8$=V@>A3~vy7AkeNYaIS;K4OJB=#3%_^Basdf)EH>e(z z9l5n!mS?>LlDQ^R7p~EG5Ef(Ck5)K`OkC0cv~iBS9{*tIrBnlpin7@x)8$Rwy;t#` zE`q&co8NVl!sZ3Nm16E~F%wKT)-PVzfbDW-ICKX02Lx(2}%X@X30f$+Ku}gHFix0Bcm3b=-RBMO#ct zkY=@NV((|!$_nMi+^M65(+O^;$ZL!<1KNvYIz;a$R-=;~`Q(GGV53OI#bu!ass#%v z=gGCAX>LGp$xhOB#ofSyjARjcq);spcp_$aDGP#Yc_5!hsQpX^u3GdF&NXu08g4X2 zy1X{q*pUk#*Xnx{!Cs>3WSQz}n4KVZv~-ssT1?DkskscYL*j_YABseAkE@c)bYq$- zFM%qJlsEK96cbTUdC7zt*bg_K4o%T`WvDI=^Z;q@a-kgNK8cF*EI}>z+L65hCH5A; ztc!UlKYx>^<%I!{s$CQCCNFvVhO+HKVlc54S}K*ED{0)qGa>?#c$|V(3b2ZhyHM=J zsp}r7^Yr`G|&t(ZgaLcNL6E=6l$tsoBz#!$aX`z-}eh4I>oUVi+)t2zEJx1%G3SBA5amWgb5M*qK*2&W7(bT6_ zb2<@T-{`kpG&`GAme;N^GH0*4w=+ila^lGg!(G9^Wg;zCVmOC+*%4hA3{QVB^sOXO z(QMYsrvsO>rb05{LSV3q0MI}Zzn340Z*`)h?f~;BY?A?C`YmnNi`4X&CKnHf%M(g% zW9AYH7ctlmDQ^`U1D}P7R0^WlbgIak>4qEBX{X8%>**Mm5Imoc1D|Z7%&sEumlZ5f z$_$03#g|NeIwF#r17#V?iyLtvYk zOpB9o5SR|y;U&1o4+e>tq{=nRFINhs6i)1z);^)#!^K}TRAvifN@*;RwNi)|`^MqM zK(k}5L1}q<;8hdLbybWsosvW820@Q8iJaPG&*Oaq8;@xyiu6863{4Swouq)VOe6sI8)~5J0(jMF^wd;vYs1t6IhE2g2Hwo}yX&s?nCeK!LcTP17`2v7!GAfw@YHOY;l1$yK z)-j_dndy3j<#D7bA>|mW@o}kU>X}9XEtRnfM3SmgzhZLLagW_p5H{{BE_eO z*FP9KofGax_;7`rBPWH@NQ7U6w@&n@$QunyJ}LXhqpiCU-bw`3D7@`S@Y?|CbU8~) zba~8Uw+v_%5S_wOF5xfI9$1JX9~JZwjalLNhJXjAF+;OAbKDu~;X+0FOLsCtg9YTi z?4Ha)rNy#$H^$wB>-V%cG0W6q;e&m9H4QGb0zH9~PpAIEwEDX0Ye0WWN{U*LRRPqa z*f(R&WRs{NlOYFD5gZ^gP)GvW=h?pMExfDGCGKroz2U?`i`Di1a3C{RZU1h-4M%kU zo8e1mHoIlAlZ$xeBa4-18!Ha*MyemHLp#0F2_|xUtq&7JZR!NR?=MCqDvwyrmLe}q z3_UDNvizcC(k_ysYm0FzSma=zw)s^jWlV{iFGS)-?O&3G-Za+wV{_hvZr(w+x464x zXYVGLimKeCxRV~i#!54xZZ|=CKiu-b*q}nK0=wwUHj4I2U|aDW$7{|P z2bu3%?u`QB&jMR8TtB&J6~(ua-g_(Qm+sl?u6)8DRPZ&(q&I(g}KaOKOBFnBjYZ!< zZ%?GVA+Wb6lAd!s-4vBJ@VJT>8IVp4|1@}*tU1*l zw0+SluEA^cqq|B~-0XkPJ-%!|W?9cp!H6Xo6bFb*1$_n!*Wi=-o7|~VT8mN-%<@4;}YyL;bqBa(vR=;@hz>R+5QcoW|mud})(AIM0R0U3U6lxt$ zwvhq6-cz@zB*v%N2fN3W2mk4P9N(+bH|5RMji}xFk*8-mCGMPtj60=pVKQUnQTE(z zPQBW)3%S|`0#k3gBKS^0Y_*aaSL>H?QyS(AcqV1-@&SjXJeozbMK|l@I)R`k33nAs zS8}Bv)mAE=T`mNmQ_*^9y9MdXY4paMjyFX4+4^|gy#5B;_>9E6C{?*kV^_E9@YlIM zu*)k%_E{>w(mY4w$t1d!DcrW*v9nt$f;3e!M4ij7iwidu7wTrDEN+pCA*eq@17OvF;_giIz$?wJogH~?t9WAfm%0zqYzgySOIr%LawdgnBdftsxT;1mSGkn#8@Y~eJR6_{H zx6KfN+_CxH4~G80%*RTaqiCmBin=NR{D1M$_iy9)OGfViRG68>itdWkp-6*-;>i)VfT%@x+-A^c~6lab(E zn|#FFSqF}}PehwM_=wlI+6><3%E}3~D_d(`t4{gppqs|t!L)j*v-7dxzs2oY1HH7R zxr#2PD|_zQphv-X)O?y%7U|%Y&gvs2abo#86~CE>eRJc>SNW^2_~cuA{lMThNKbgg z12que_UQZAIKHoYn=w@*v`lu9+c?4Q;c5Jh*W|s@{IBvmd zs$VZ+{A|4nz!g3J$ht!gz=v!7-J&7X9@FB^W9N7IeSVw69wKi(3VZ91-CYqzXudhe zkxD7diX>CFV5S@c#YkXA>D@wq8w$1!Y!kT>q^W}W^H=Mu7795ue|g6*uY9k~#C^_SRY7q+08nIFAE_Cx#~T#R{rD%Lr+51lB4#R zj;wl?(o-OToCcuogwSglln2YQIk$ls*Cw=9FJ67&$}4%M^`q{I?OmDtyLRH+UgSE# zBIE&@1B@gi!xj$(B&>_k&5GzP%le3SqH6dK6?+3PoNJh+AYDU#Ws`FX{N!~Jymh{K zmzBPie$u|Ja085g7Nw~h`tDj)4K*~r+|q_)vlXZ>U3H@PF^u~&c2aV zRJvm6Uw(kD1o*qBTMgAfx%fEy4S<`->4@S1sJAdS@>5cu zHWJPg*PLvv0-Qk)dXkvQn`9{yn^mzT3Yb|ZsPcTLS@!(9(odTdfNbzrmqv#jKo6di z@e5Y~Z-}7_a-Qwph1)NUPN^q=p_xIZ;2@ovo2+=wXnI2!4t~D*kJdT^Uowu=;s!t) zZ)(z1sgRW_(V%(oL38%*}NV9V&i!|mkXZ?Uh}E28G_I({-3N>Id`6E z^)tRd9#_weXskT4461FSP|V!YBv89Ff$plIQ|>r};~cfnLvqNXW7Hgv1fdParX5@c zSvxvcdu+Z}RWt@o<=`x^|71f`>L9*FcIvY9AmjK?c0QAeVECkJ^1afIsNFzpvmP)J zP(-K2Rl?v5Nh-QtQbwK^knfec`F`kCXOK_ha1_yM)_xoF1THj>p^d*?uxn{8M#=TTrcJS)R1=g+ zZy+G6Zf}L9$ra{3;|%BI-I~NJcYyEblu@e_qi*X!7$4kM;Ya_k24T!b+o6kIpixZm z$1h9NO_Ox!>M`3q2?euODC#VluS6W4_p!|^&sW5td8jj`PI`-pU8WQ2hPN?=W{0c5!8mIa6 zS8iwEk92Lkd_73+G{drF&+c9Ip{u~vKxAx(9VLZEU~&!83lo`&HaJX})M6=xGk-oK z@xh3j3<>Q*G25YYG7eV;@x{FH8jP*}IJxF}Bc=51Di~jWvtF3JTaIqyRw;U=81i&J z{?+8G&Ln|9oefL+1f9SVRp8FmusY)0utJ?>aM@2=9} zEzo+4ub+&H{#`s8jrG^I^7RceF||LLNOv&RE<@mXWNM~+?IP~YgeTRNyYfRr1(GXN zy-eFLBg~(yra?T9$ZkL}j%lTx8WltB{3dAN0 z^iMw+`cIj12CvW(h3HV>Y{!$r-ez_goZp7*H#R4~zF&G#TjF6V9y_Ca;Z6wCH|oag zmHWK{jMQ;Xy_jl7_ffg5CPd$Fr1vw)?r*a$I*kJjC_?CUJzf!iSjO177PJ?Sytk%^7$CX~Zm7h> zA|$ejAY%tDY^kZ(%2RGU_`0=*oDtfPSHl>5<~Eq4$wrP$A7SGszbx-a-qDAUdA}Hc zSOpGq=Jq4Dm`&I}hqkIr{J6Z^v@KzYc8V~3| zix+GvOv!k8Y(m!-e1kcVbFB}k<>fFN<x;mNLb>gbw*a@Y@3~^Aq&l2R}bN%!Q?2 z1j^$L@@E@F;X$YxyT8^vULZ$A7}0^gYae^v$nC1&W^M4zVu}8E(n$V!Qip z6ZXz{yk^VfCcO|V`A9HDn1gBnWk`MxtvO2|OLyk*4k@grG2u~wTCB5qCc)|KI=wP7 zq=?*3MRx;1F?ao;{d8LW+NFO3&xofh|3MHx^pd~)<3FtJq0=hR*cpdur;Zh8b{>TC zgG^P%qKEc%4FaG>++@Z?K|qHkI)_`WD)HAIA5qbbG=2SDMq- z%im)~cyYI>oPK9IPH1%xdl!$GtIg-ipx}R+Kk={^$H0FZPtRB2`Ke>ibZjutr*EM^ zrc$yNnM+Te8K&BG(WpbAy}3w@z~C6SI?!;*R4zSq=9%-kI=qS4hV0;Kyy?P3u|j`)Ao{ zI6JxaMlI-*FMaoA3+$-QrM|~s8-S^(_!N%x8E15M!mGNoA`(l5t}Ji5Qu;GIl7;iO zv+&O`WbXCue0ON^GtGb&(d@#3hcCtfGk}q^HZ4xj&M`aqG|=8Pvfl;74Psd1Widl0WoD%l6;15wkzI8IsD}^_% zt$B`hCP3e!y_r3qx87MY%KR=PtmizbHr{z~Q?6LsjXQFUw+q?x1|8)2kA&=eeB>ut zo~cxOT5^Nut^xFB*vt*UY`0I(_0V%1@e+@FitB$L5EuQTI@Bg{GoHI+)5%;=@Z9QB zT`qe%hQ=dwG4R}*@ZH4?X?3*rQp)TBxg)8+o4MV5=x+lc`*^6{5B*KPvrePcS}7~_ zz+a%sW(xfvy^6h2s-U^z>JF;6?AW^W%r=G&VdmNUIKEeV-$6Eje5kJM=Q>VxX9{v+~Bo&5Oo^p7v{1+c0V3)WD{Q8xC`)0a-25k8)GYMZ@UmT&Sy zoV<&-_^}Bfj_w60;Fv3zgR3no2UuNoT7eXnKvJ2=LKh4HYwl(XeE2ST9OjHJPv6%z ze)D@3YJAJqwCc>p9?gEyXjzr&=j9J~)A!kD57(U{I#9hVfq|qz+B@vb6@RWtf7NMD z?WVCkUayWndX=pERbMj~Xm4dMyjw9VBAmd)_Pe?}{0%fOGPx+L<_f29gu#nEOlrXT z^e-~WKd(!BvwHkyOoUeISH~InZL`^}F&w_BL!?-b@|wt#HBJ~5Dw&iCiIQvmcC?7_ zfZnZj(9EJ(7DbNwTj=}2)69?5L^Zu`VqTZR!R}fXNa9A%x=Sv8lYY@)MZA_|3s)$a zKJVr;hzv@ZIMX$gO0>!ww%5rH^AyY|a$Iom%=<9D=K!O=UQ(@;B{$7pLkBPG%|Gl& z7d?Bf`aZbU1)vw=NF|j>Gr*BC`MHa2rc;wH|5-O5`z@cb2fb{{6 zhhUh1ZLCasbCuiHZMp~SVp__(PNxSZdop6QTk+r$g08+-I_C1*4~5=vZ4e2oD}tJi z3i%~=^m`ecOmdy2PiQHmt=ndV<}HT zoNfT1zFb*-!)~RCVYFVng|{@d2`D@8S7@|3!C8<&0Rz3cQ`($gjnFchj!%h{x-QPMClGN{o2O-E)^t8GQpA!cJ>CGT> zdHatfal-4J&pl;WjE$(3Knc^a_j+G-pl;lPaT1`@z1Y_GGaYZ-aLL(;|%E=eJ66Vna6E8OAX7YI&`D4c(sQCE2yHdO)c%a)T{1Z|} z%AxUaH@}|qx3X@d5vP6>p$8IHKb$m<3XgFLalXw|pDE?lvfxj5H^fB1&Y^N>)XN52 zKmiX(Rp4br1k|X-`kFYmZ)8v=G2fZ5T-=z)OFcF0(f+x=I=?e6I*slZr_)=&f$B4u zj340+QFYCACTXzH7BJj;=;4oXa(7?*^MmQ`X6_;6JzRd##D?aRftihZ#jQMMQm08) z#^zsUQ_xy!_XxSN%hmk; zWt{_JVB2ZJ{%6!*znVAxkTfebi8f2+>0RElg&j(R@sSQMb^J8Wh07V-^Pt62$9qCH z8X!~Z@(xk#Idk&nAvdkmzs{oHsqVdRa+r%9Hs2L`i`FmdGwQdB1@cYhXO*)!K_AHQ zz3SR+`iQB=gAB(2eyKQK-+ERpl~tTns=!5_aqtal!35e?P?&o_&jy}#ndf&W;qtj| z-hD9)9NOfuISP#&aJ;$`dPddjQ=^J>_N$dRgv%?OrdbCcqtiva`Q#P!XcNf_W=2GB zBZ+PX3|=Ztoy}lmz7c_WWVd7J2MZWHi*7Drzlr1~U;=@ZWcY0Rh124Zq5K*0uyTyPvvw2f6 zi_Q08e7{P6xD#vIv6mHg(f|M;07*naR5sO}Pld+n?tF2v`1Dkt|DWaHyOR>~ zIktGgd2iyGUAHe1wmX`2?>u4EmKJxmj*L%05t#FoL)WHCI=R78dLG{oGx@XwVvbrO zZAe>W(-S9NMD!vyh6^B6JXUij#8upjoBx2j<4L$!(mgl+Z1J_TJ-BG4P8XlP)35$$ z%g@Keum?Er2=5(C`0%(b5Zc7nXR?G0vUf|wLbF71jVxK(N2P=*1VQtt%OPx5$`Fe5MBSL}BW4 zc{zd1&%5Kx9;o$?A!9%wcQ)K*KsB?}Qiyz&yOIOWSyJ7X*unuqvVN0=hbi^v*`q(L`*yEKil z9Hotu<#aijb*&%n?j6aNIFHEzd6?6G4IJ#6`*nJHTTijM(4hbHA4)IhTaP=`ZF;|E zC|78+oux+OUyiOGBel2x%@}0xSj2}ubD`NYM50K*>}ByePvUtkPv9av*Yhf~HGWme zcx+lTOL#WB?A0W|-o3{{!2S|@;XwKu>$ialtW*=o`r zH0siWm*G+2C+VGKFTZ?Jd^CVRfbf%`2rVdhfpJY-a_N}O7YMc@S`)YmbK@nrw|m@N zv?T-paFmNzc8o`gX_ti8k+8hoW7-5Oys@F43j|W%GL^X*%&1*7fQ-`=F4s_itVuwA z!|PTzHiq~5$%Cpzm47mMOcliFhAUSKwiggP0^;&;nUNVL;A&(;fHuc1sd-pE?>-Pb z>iu%r-V0r}0hr+%5P<;B%#sw$8-X?$g#Td@@MAWp1FCh1&xB+73TQ4|F zFvvU6!&v$gv7R|j)LSX%U)Ws*&$l&muqiIQLR-z4cs4u7A?Y#R1dMxT9C=zXc0A^% zLv&>1^4dK&1MVPZl=(T1P8l{9=&2aREV`Bpz;dd(03U7szCW`&jnX`#{|-|LmWT%0 z)kW>ccHnzHeK8;hR+t%xqnD5Po)&bNR54k(w#6?P7?`55zQfHQ>k#)|=I{BQuE=;x zE}37!Nc&B;_D3)CRcCVD1-;MaHhBEof9^5gwX>POAH2E**&BG?9vy}k5iok|xOgEx z878tUPwMP-689GHFvNY|!zZkpsiD$%bwExNKAaZpVFC1K4NI%34&Cb(tvh44vq(D@ zZ;zrmGC2!}@;e9p!!eb=-6f*6P=DJuQ7TzlWatSKsYb z{)?JAyUTS|aE=70(fGtGJ0r`#Xh+Evpw3&etQ(X5WbC@|pySFbf*!P1a}n?3jxk%O zFQ{4jg0oWmbTA?PD}COiS_suFi-RLBRUv^FqwLz-tMUiHf)`--HrBnd~@-`^)BqtTPhJcmX5TXSliw|vf zd=4NTl)v$)WvB^(WuLjwYUs$Cr;IV6?U!D%{2wf}v)nmdG4(OS+=%oc;zw_p8t)!b z<@yla-f69U1XmtVnovdS*=oHITbXuBoJ&yCt8a!>%}(!(a=iSI?ReFWLgnh zRtiOkG(f2WKoTpYFgH`Mss*|vi6jTD$jD-aEV&lySQw)~D$N5}iN-frv6-e3FB9ds z(I9vC49T@8^x2SGDHCW#DW@nreVbg4vZL2n($V7<@BCgTwMBXPf^pdJy!aV-ZK>v%Fthv?CM*l)4eE4dNc5m{| zlFGc9KC%Xf!B=Ds#4fxE2EH^@c^y#sxxNIIu4hhz<9v2CKQwFTS6aTtu#C<$=O|M` z^3w>8buqSZOB*zFAs%=Y?y7k=x*v}QCkrS0FAw@T!@Z~caSo22SG|Jvcfb0`_)Zx9 z(jCz;&~P9W+m=5^-UROs-G2+S-#&FF&_1or3XT(ywe z_S;{kjvi4Ud&F$Ct!h4tF-T@72yxf@f?yaO+m9(wQz3Ox3M?|} z;u7SP#jwr}^NpZZ9TR_3wAf8?Yr|iEa{TA_I-9#0^2gojqupX!1VcQoRSPj#nR3Wt zV6Wj)K?8WX1oH?^`{fcs=Iu=&(1i_OYj|;8rG?qDxbjIAE=VzpI&&GeJ!qg$1m{@; zQHVKEfqCpm{QWmidlgDsAPa6cF1a|Fhqg(l9|Lb^ z|Lg}t-(Ia;F#&)v*C4z8FCurd5OZlhm8WN$!DthjZs6xj5hj{FYzG(CR%dM1IocT_ z5B6|1)65V=rU+#CfESXmnRbs3Aa;vu$H_8a7IMJH=V`O$Xt#;nAF$yq?A$EPK9iZK z&6!jAjF8?LvcEVxjAHNlexZI!PajE_y5 zMPVt6d;2t~3pk%E24$$oF%Y=ct3BHM_WRvfb0{Xo~x`2gt-P%co$;!mHx#KG)gHqX1_BIofP6i%fP^xOYme z;X$=$NTpS=KBF5s-%i174XNe>wtz!|3qM*1A2oo+n(#7$<~`=lvdQqWpuV~uKj*rg zyL5D!R$kKcFW~D1eiyw`s*eunYxmcf|H4!E`G}~;^5<_evdTb3UBKhP`KO%qg zf32slA9Y0Jzr;J}=qKf9qvn zQX;G0kgML0;cql9uwR%?6FBx=2|2eH0C*$(X|{GUS(pE^^N^YF*le~3ea{}|Xt=er zb!VlT=jq3xB)n4^b(wRS`rnEKVW0>{P5}C-uEv^>-_x6u7yfUEZU) z0=L1$JWrN4RWxh!RTHan61EOU(0r+$wESI7fIijCJkt8j`DGn6#k^d8FRlsje+Ehp5 zt8S!RY1YtMU9RxfDQ^wq;6??szR;gq{;c2ci&s6sc{M5PmJvAlx%de9 *)KV3zE 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

>TJz+FA~b4+e{_&yjXsfSpZ?P2UL0}< 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

7Ki)eCY2Sn8sNmP&@Ky!MwXR;r)i*S5J>J>Oi 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 zËPi6Az>%>(vCLlVXJ5+LjRAgWcpfbwRePp;@I}ewqg$%><+2T72wC;^kwH}uR zmkR&%gP|8f3q)p!_d3A)lU)tlZQN0_LU@xyjaQJ@!BcfGG7 zu406;>I|(l>;`u(4NRH1R={Rb=-M^y)-us zpFSII4UZFxmg0=Zjy{dbbsRYEr&Ss7I*Y}{fUvHS;d3N%I5vh+X=@%n%4Dm2Hy#5b zHHj&aPN03~b(?(|87$ksiPLZE%k?uuII`5vGMfVOZMFH-Z=e=NV7k@ruq(!W9wkQjn zZR5imn@4wT?i?2a8r*CUGz6!|C|>CYW6ge)eUZSklyJ?)ai6D1j$Vko4;RuJP<|1+ zThK1k8zGy_KKS=##{TZ-SFg(^c(^x4~} z`aWQt<7r5R%Uu)j>9Gs|ATQv38)Ka0QX8yDxoX!hhRZ=ihT|WJ+1whn^d`PJQA!DV zo7LXpu$Eqa7f!mx2gd^`S79)LjL+S)q)V!uM3NEMqgWx3aaq3&Kf$kaTr?x?Dq|C2mHR#8A159ip~auac>;$pVR)yI3npF#u9BE)6modBlh!Pqw4TK z*OxtzNSA|oCB;P~bc{WqjTGxy@px(t zMYb}@0w*t(Vty(LCYLaCb&jqgziw$m2k8ASsW<%A;p{#8_e|vX$`p&d=2M0w1E*$6F;`u}=&AfbLEl zSdFhsJvYMGonw1d7rC3(VR0fY>-Fb>deghC+=IODkJecAJc@(o_Qzjqhp|LQam~6& zQ;TEvJfU0S*FhZ^WNE(}+e1zn1pz?-cUVTQ0E_8j+Sc#nXWD^9SH}M9Z$$H>8E|8K z_vmi}{o;Z=jN_4mGY@Vdv;BAS)HHb0(pI}C=%iUOnq2SoRs{*_S2zlo3Z!@mNbwqI z*OYy@t=yLp|4NRzrg+_-!P2IlJJ@|kPv@nuz6wSs&{{N1PDhs&?SKBa8|U^{4YG_~ zL*2J6|IER^#8ytlAS>Dk&*E1SNq0i(K*_fCQezyDBBBKKgbHS*Iz}u2vEl0^py;#S z0(Skji+C;|wgY-Ga|f^24q71Ne_8s+l_jU|#?qIjKx_WA6v1g@u|OtjC@(&uThp`i z{ztexPJSLST%+F-&8~7U(g3%UImmuN6N8jCc{3u%NvWw<@pZOjn}*Dgv~!W-ivgy z)7P)6a6JNaVu^hlJT5+JP^3R3?^7>Rb_DOJL~sDFjWF^ETwP)mNQ@9;jWNI&Iu9~^ z!-4-8_Z4P7VVG$W`Pq`sUWui(aO1Q0Qp-H@AQ|2%FEN{b|KnKX-SW_4LwW<560Rpa zKq{KOF?AsNxV{n$qUIPS4N(ON>6svsF(9mntbh2`;V2Re9|AU^&p)cH}*Urlt7qLrw4{&o?{=M;B| zzC8ET8}()uB27)X(yg8wF~j#MNO5KJnKvk%a$0A{yiXxf9;ptv2M0YA0G70A5r zoGm>t5#l0umr1_6A>m6C8K*WXFqMtC!KOv6joehAZAJwC0Y2Xr{4G**N@#cwBIqYj zj8>gIf7H;7m%XD)1Rg|FpF^+DU>X_D=0ky+d`o2BE(c61>0u9N$-g$zkBvxff+kXZ zrotE%Vk#tCg*6*<`|L7GmNF46PG1+HblMY(WhYi1N7hU|Zq70(zqp6k8!`KSwiVXk zcos`;Kg?AtCbeGs=9jj5-FtbGv7XOoFQmpBEur**`6#_QxE69US%8FuMXbcpd)Tyw zko`@RXQXJUr01dFi7T#FLjt~zF8%TcL%*~5a^iHG{qTM4PZ)jdn6nuODSh4=d~)t2)TiOqXd@^tE0)@-y4=QCV7 zi*TGoMQj+VFW7;c2(P6YI3U$CeI^APe=zX+WmId@m!EgQ@7r>Hde*g;US06t-Ai8>I7Wv zCQLKrNoVk9x+9LQ!PsBRZw@#iCT1+-c&aZ#mIjb^C4Jpx-9{i%R4+}T9EMA1id`oI znkbRIN_Yb6UlZ16QE3BvmVoRC|I;oqE&6fLL0~ZR3V~J zLITC%TDFk?Zh0z0e?FD5{Gb{ya*TZVPU%%TkP7C?+y9#H6w>3!rVC&DD_w{p@P%#< zk~J9sV1Pd2xnK_+Vwkr)W_B63hv#{hL3j#sVy|Jo>*UDu^)X5 zlDo+G35KRTL*6#>HGyAV5HS{0s<8#cLUfhT)+k^n(be4U_lj6YCWATFxL}=;VAKtc zz$n>IA)Z}Yl|I;|s~>XMdw#&8i4A2|1ACc>S|q|9wo!UNarZt({yWWk@9}H&-58Nz z)(VEUQwrzXu~7w&1_8C8WA;`KEEG*Dpm9HIBndU1=VxWhT}yVf+?zjX$}jo(wHo^; zaqSvl)$cmVbq#r`v8KVSZzQ36%=*X9(%osv1y{#3nY5f7)6e0vmB^4^Lx3WV)KcTs z6@@53a*a-JxK?HvLu!OwOu7+h;Y5%`F_Va=Hdrh)H%L8(u=!76^D%<&6u+I2XaDTf z{}UxDCM=cCd0Wc;@*IvfIW9c+y5 zoIlUh>qAmhy|F30nW0jxO45Aawn7}%TRxeG*ztI3GM0rd@&=gZFPs%N)>|! z4y1RDW~y^2l*JZ20H_;@JpFo3vKyOPC;)IUG63CkUK$n^5wHjv9b z)E{h9{3QIc-LvDnccTdwk1c6b&awhslw`T95aqP6&KKeY$Dzp_fn+xdo7?Gx>qQC- z>I$WG-y$>nj1OhD==3%n7L)M++kp(#pMsUj^;nx`2pexaf8BzfA8IFUlZ@sn3J{`k zjsoB%HC>Gcurz?`Zec4))9LwomVW-8ZDPr*sO`-y(wUfbhm!#Wmy{4 zEe8~6oq{PUn@&V#L0f?|dVBL;98Q<+ZEay9>8O2hJbc|DtC{ph>9ch|7R_xg6=K!Q zdp_@S;j%+orIG6em}-Fpe1GdI3<@2|yVFx&q+V>Dxuz4O*5jT6B!Se3@%$zdjU+Z4vvzuB^n5*bfBzc zXp%(ugzTEGK$JXFV6#~cF2(nO+yqi4XBuq(D`xFCQ?vQ75ljZ(1&0oHbEYrnvdEv9 z)}q>r%F(DYg;TFSluBME=j3IY-{jJv#wJm$Vl~QuS|+9B(qxg-Oc%iUq+*d0d#|A9 zM2*e|I0f)RLML>cuaaomX0SqL@aJb@q&7*om(MqFF;9o%Q%6!<+`T*-r})X&QxmNm zTkWB;;w|lt9-3RfyM|NaLxd=%u zHT?##k7VNcHPCE=*<)jgxteFrtl565`f?h7-DXFJki6e8J6-9_M@LsfLpl|D9b0US zl(`fPM08O_vvDO(Pq?LUB&1Y?Xrxe`!eocWz2)>7?{~Ge;oIC)+&m&*RbWBA-Iz14&nwl-*01|tKQoN~e0YVcb~ z`-YUAPGZz?)e9FfAbTK>B-Fl8=72cQ8~vo#WAHkuUpaAcPF*U*VBqRfCm1))F|{EX z$gwWFMjM=%2e>Q^7A>It$kZ$tdS}Xqp8jlbK7KL#a6L=&p8_G@os0 zFX`*2LE2WLDhik*yywEeA&Na8lcz)QR!u{?J&X%{giNHft1JzFxbDIU_3`R6KcX<%!*Oo(Ca>KH;z2d}J-ur# z5`i7fUUzC2-JCn3Yhl?3gdIrT3M2wPPa|VY&@;<%K7~qa?`eGw9t&jz$6d>T*<=cw zu{0B8%40ePKwWta+@M%FTU!IxY-6p&Jj>oAZ6dM zYYq_Qq!MbP!N|0NIU>*q?!{xn$VMR1%7s8$Cc`9{JW@>!mftmWUhU<|_W|~1KujYb z)*LzsidEG~GUqVUUS%DMCc0aH7Ee^uG?0t!?XvKlsG5jj?=4+B8Cw%{I8XGe9XX@D zNohT&g3I8-Pu^-9_I8^yRu?OSa(*iBK=v*<-rP{=HLZNVKyYl#fd`2Pt~ntduWOO) zpPgx+@#wo-?aB&PrK{1n*iD8ZQluw$FrdN*Os{OA4(6%kRO^ z8yOh`ZYKabQ^=VX%L^fTl!Ca7=bdAlYVg6R_X#Y&<64g&S;k{L`vI915_C=*A`jwL z*MF3AWp?kn{Rr@vHUR!U2k@ClRCb~?jSIwl4-kbkV3ZOZHL=RT_qHMpVu`PE-(Fu* z`2Pt3-_rJ<2GDQy$sCx;t=jaNe~(wtYbpmNy-2MU{40U{$aSRU$?g;7Wj+Idr-Vs~9v^vZ6C9im_IZw!ko6HD?7J zETq85s~-&gUx5qB9Y-?ltD^E&u+JIoXa=>8jFX=5#@+y)daeow#hCyAAOJ~3K~&Ov zzOO(R&sn;2nm-yEvMhDJ0pypz@xK2;yXnedeHNPa*!bl+WsWn$OVmjT=>)(hoKh}A zR8o_vr<1WUP`+M?2a}BYiYvTvpZv>g^Qsx!&r`EAcQJtJMt4POY@EHu?7E)&9@D?? za3}hz_mvbm>MGBM;OnrAnm!W~z;+ z<;Eo%Y|?8>UOKMd-JsNsY#-9w!Dcsj_|~r-BI%nJ89%s5w5|15Bko!B(_zO>H{8Va zxF+_HY2VcQCiF~$&$Q^lZ5(ek$iY@AE{(Gbw8nt~l|acj9a$B_@Cbd9?54Pv*skdP zoBbEnI80mW9?%&s9bG*D%&WacK>rMdHEn5yhPZ1CDV#IV)wNYn+rT3;PI^=basmx- z(G9OKL9;oh(zSMUqELy2xxe_9c=u~zXR!Y2@80E}C=)%-_yS(M^q0PJV+zFc7Umf> z_w9$}XJ3bra3?${75V_VVI=DdVo|RVqgdMY_;iCY@IrcANj|sZkv9P_t3TLVDCVbT zHU^}+`VLq4)rNonj?e^GPU|HBzjJx@*XcNs{kMOp`jKtaC{0_NPo8EkjihERAvDKX zJuDfw3RAMDnY+l@Cn~B(e07`d@{9%;ky`281SV}NRMM3>#K$#)@5T%LsxJdLJrPS+ z&+x7D&vTE#T+}F>PG&naQ3`jN?KY4|nx!bd`X#l_5cx;{e|z_}BuREA_}#UZAv|}zjumOMxhKnuFV59(RS?Jkk zoC5u`jRj#P4Z3u38ZH0S<9B`Obd&xEzR;p?%BM2+rF3=l1OAniR69QSLv{HR=EIX8 zhp4av?&R1ntw)iFq)353JsoN_m zU>EfFXl=h8!gv{OiTX3ZE>j_SkZ4UT1onB&YS4URIw$QhyZQ}D{c640`)>q)rFE|S z+TCDhlKoJI)Dz;)l0ue!gaEoA=7SJoMQYc>T3E5^pg)A-)}KFH^p{#_ zHx&+^t?`vk_+w+K--Y*U?xx>_KcuJnl+!;#QgZPc2=wWqq5KY`#_B^q`S31^z2D>R zZJhpj$~#_g6OSS0BKGYpt>iTV-o;6p&*2*W(I}7n^I5Q;QH+*$jdxA|Lv!a>?auUG zy4mci)0q3Ch)q%Uep!CFjohKbZHCFkf(>tI_Ptxz{$x=z!-bS!IN@iLLjFjtWOF&S z7J(Ty{uL*#2e2KXo8_uNU&yM#A}_qZi=O^HvJ*U~7eBw-yl>vvXY*-EMk_O*J+n*t zpI?Fh<%ApQ@H(z&Fs$hqzVu|>LDmWwUOKqCNM`q(KSA-SD{Vf8*m$vy;dGehi(wfv zp{1REw2VkSe2y3()wy~oMEO;Q0?SY%&( z_wi7@*tNc;7(F8&$)4%R^)az~hk>^n{Bogti*=$*;mM5sq}}_c4G%2cD2%Uu9hI7^ zVa8~U=l_P#d)8(ZxCNXrF7WQca{Q#$8W+#+cU#~;mtd@k^4z}XA>I=muGh{y&x`r9 z#VQ395D8~R(M~_cf|;xi@{r+4CJ=X=0D30HmGS}s&>nt9TmK#T{zqSt!Y{su{`>Fl ziVfvQUtd3e_`^A;Rpg~-k3m1H_SGzph1g9E2K0fy+8T$2Czy=mvk)`$zJNiHBZkzh> zf`2TRuLror?5qm>bl9Z=TqO034FikJu`o_74d*b|M)g7va}>AFhGIyPOjY`-A0je^ z@?3RSr#gR>81_X}trUFy?J-0Z?$(Fue123P!sxolcSm(1-2cK8Yk$KF)e7_Ve)tbr zbo#>&FVA0#|Ka@|z+2eq-OgwXrjCF(9xBSC74R8{2Uhh>fNj>2a#@13&Xc8dLNR-t z{R1U`xtFzn445yTI^55=y<k42{*h7q^?~?_f58a*qxUnQWDC&O;{*!z2T4J zcFAeK;M=Ft{?nE9gO~sI-Cgqge?nP#`1cnN-V!9O2%HGZHKRm?u^q3I;cYV*p74p~V;O2UAixl3Sa>~ahjk)vm`*5L^fWg#! zM*trOb@!o!^vh+N+lTNyT5QeXrv)$n0iIQhR`Fb_wS1(BvGtsdkw~f&nbhq{DBTi* z{DenZtbGWy7ifU}{L2hK8#n26s0!DCc%yX^6^#cg?A^eg`P1tPbw3k~3{1p7tmrt1 zoznHpDa=^L^f{%8=h*j62{16t39hQmT9$-e0WK(f5?P zdb*hoPR73{Clf=vwV4m!ezgBrBDj#0e;F_zQ0mql-YwE}HB(mJ-1v)G*$l|~@X^@i zcXw#y0H-T_UJ7Vd_{5!ANky=e?bwc~6n;70%C&s+_3Msx^R@`Hqqc*#Q5m*}&e0y00q7M{0 zhjfKsT$AE#aBZc&c^mr{<=n5+;NH?ySig)h5O^Lr@8|f`E@k&NG|($cL$*;{ z{?ER6HOH`z-$6g&cH7m;d9Zgx1Sjo&rvj9FT#hfAKUGRSQ^Q}3oSmdZDy#Zmf(&}C z9m%TXOq28hLT<~#qauII5_6H?f41oVQ_JAX04mThi$P-dyA=FTsBV0Wju8CleG~}Ja_6hSVb*nbKjpjhB;K#31mL3on&Rt zmTla^**SNl>IEogOcf}{&e>2eBXT*>YLe_QI=olNBvLJL7Z&;!MwYHsQpy+XjR`A|BCaYm9t)TRN*wCR~cZ8ph0BvPhpE**O`5fX1)hkmO?_jk+tchifDT)nzQ z{NFvSRaP6cK)cj3)OKV;sfSmSgf)W$$s%g*SB&fNaUilAnga^X4H`@m%HL$LOn<&7 zx|UQv&2%G8Rg8dBh;Z?oKPkx)-~ao^Yp-+UHM&vAsO*Vf?}nAK2AnKpE?3Wf%$sP{ zw+SW`35bKNjOhYYWlgb-2H-9cWC7vug2+=ma=x#~{@Px1mB0Nf3d8WU++RAA;QxK7 z^d-(8VDp65{5Bz7O-&VV zieVTbK}iK(A})s@V$>pSl!aZWy-%|TJyV_bD&HYRoFwFku{dCYTD08JCpq{;!RA|#zilhQdpsrIJM^7Ut zw8f;JaTOcx@^HOK)rcL^l<~92sV+RnXH3CsMI`OHtph|F%dLD~$&&SYgV|Pbpf@$p zbgfY^S^p!rwpyBeu1*f64~8gTM^i8w44#Fzf+JUgKwPNN#?9cdmbBbdK-G0M)I}sU z4K`<6Z-UA%>^A*>3G`1J{u3naVtepBZzTrc#? z2V*%H!CV1@HgPlB;bMz2Bd1m@WFjkXV7wf=#5fQyuDQ&00PjX&pv4~d1)zh-rt$^1 z!5HR{*X4eW49;f{@4^(l75Td}Ak! z>QL(?dDCDEFEFhkG!3lIkyyt>+gAsbTb4d_$!@lf8W2%qKVNj&xTVdEUdk$$XO4nO zWH%@y22(i%!_`sGyb1SLACUc|_}k4_3bbe3v}=Lueyl$|MvB1d1LU(&76&N`FPtnY zFi`~Yu)xk&ATdO~p(9*8rQ?Y7(NAa4#RJ(*+_HMa0^U9v4+i!XEH-F|z}_C|R=0nOpWMoqf~so6!%vDd}t!g6vwt0wCH z-`vtH&rOsf%R0Re(v|rqU5og%=z)ki{eUb#rc%&vCHIZ#${ZHJ`9`Jqn~AlY{WVwT zANP0lSigJ>DpP*mH>m?jz4B+dwnuGP*Ij#|r&U3JO@cx4K7GF0LpstOL}3J8h>ML7 z^VQyt9j7|)z4+Ewp+%{CoktYsVew$}q)MC11=U!aeR&!Ii!)YX9vof2pL5Be5{KYJ zyqN_-*u&-5vsEbxHrXn!1j>Wv{iVvr>l9tBHmRcZB8S=xNNtw!FrPI3mv;_9Shar& z+XoiQ!_c`aUZbXSDleXoOU_R|V8HkUN42$kjTh##p3YjK0@+B zjRfmEE&{DPhXj;|bL7ypD%oSV0+hbTpNu{CHF0iPx*sLDZqD4kpH9(9;IA_}UpxHg zi_UZt7+(Z0*aeTlg|EOkp#^DCqGa|2vARC;iZ7h(%)U9P;dJd0V)uiC;6|*pE6v9r zRA%h{jLkV?QZYbIKv!jwZHOkD%us;!VK>zrPH@vYeyGM6Ozi@fb@m@D!vh`h#q$PG zAV&+*On#B6a@YAomr>^`U2KNpQuJ^^UIp;JM#s9!cOVY>Jbe@LG(jU!07hzm7HE_-QMQUbJi)EI(E zUc1GrYLzr;z)lRF?6~ePgI@)F|GJ5&v<$zSG2fljW>>j$bwXV0jk4uFo=9&h!+1~I zD5)RQY1lYGsz89i#149+ac-rB8Vsr;Cw@Yd)8-1OE-U<+-n19UC(xQs` z#Z+Os{Cj8D3o+E$AVtHhd=b4?i{S+7powPx;R$~gFXAKpJt9<>`Fv;DyUp6Azi{6B zUtFhAwCEPunA#^;i)8S$4NuKaivCIG^fDe;4MXMvk0I10wLc;aX*juuDZ!%eUNO4lF5=5R@-kD+9(x%g6Hlu>Z%<4jU!Cj2g} z7f+4ZaZ&@z856oE-Z-gh8!?KDA8!Od#!JeapF79)OaweCQ(Bg~1N>fx?l-0Qh9br& zF}w1>%lL;Ivv^_rP`eaE9kq<+Smve2M=+If?G`1bYdD|!WRmaLZU8@euSYt z=tB5PT6JCZ$dn1Gl%h>6t%a6Q*P}WYqq+q>_C?_qqKQo+60^`ssoS(@Dow%y_$q+O z`TyLRWX%n541|=uu1cH)<2y|EfSTdr-Dp2~tVqk=QzjQ!t$AitB!(4Tt;CF58vYWC zyXXzE!^4%>sby2Pi^+651zUzy+6h^{qKV3=IQkP=elthFCDAjGiSL)u4?SaZ3H*7nMp8#HIy0ORanA1NEON3(g(rN5@$#V4^T{M^k zs^N{!ta{e6^ZfzY68*|}A{7Y`f=X=KMMqqrukU(JMNFlK&lbIft_OJ6paX>! z7n6ks{{FxjlJ$kbv!LdWAW2qZ?LC>_bIbdQ(w9?UbO-h?{4wF*PE^Ezr#hi|yuJh^ zY*0o6f%ehN2y3iK!3)QCXhGG=fLB&lOd3&iOr#D0Mi#*8t9ZdiYa9l&$E>inib29g zbgwv*s4-Ty^)el={A+}LSP>KTXz7dEO0K*gm(r1(4z7|92rwAAfQ^smNaTX95fyQ@ zRS6g*fIeUJ-adlxp3<^sV)~H~wE(W)MvHKuO{dOa72bx$^xdfRVcHmvIdd2}cPMw2 zHAZau;IuuE8p=Mbfb>)a*~q1$X;%1X>ouA$MiB8*=9jkBCR7#!$khrS!e!=WAsu8_Vxy|8|T#27E;sw(e)OyA76I)5$a@ zHgx8R&@`;+F`;WoFhooKf@P9i2vtw$D^MbQjk4&0K{tqXlVFbxv!7rZKYZ2i1@h9vyl`Wl+&Q)|yek zUBzEO^*)b32Bl4oSpJ5XXC-Gv(qxJOw;1zo>UW}+lod*9<2;@<+tT?VTYt4v+~z@N%|}aQr$x*d36D(zZ7RbAVH_0l<0~&UC$RYoF5blQwdR2h;Ub7KE^H zOw#}HH~359pCfS@82h3S0==7Y?InD9h^-n;qhyw!JCnVdSdQ7}Klx@=EYrt6o#Ub` zJiQ~Ow97WaxQUSQO=j)+RJ6%1vtZ&zWo};4GL@pFfP=}Vox&yabo#)$2)%c&8d-fO z5>FaH`}Bf&19ZMNBdyKr0ILzieL=kbWe ztLx+lBn#Y=tn{~Xg^Aqzw{`g47k7^+KUo)ixn0(fySiJAfu7o7+v}VNkrL|xSWcZ- ziW%<1st)&nat{NW$)Z>xVXhG8l_Q~;)Ea(s;Ed!^5kQV8?Pia;FeBx z-_k)H>{^%6u2JmdSSR#bBn668o{+`Qx6;XMd0Wa>qOG90MmN=mr|1OA0M^UO!uNS2 z7{=(G8>T$+Fg@HXj}_y`of|g1OT*|MO@_&O1KECmQx7dSoH7|n-9stuD zbKjo;kZe*KLp#g!T&=C*+?vEO3{c*os!5*3Ppt! zld(r%SlOQeWbzIiO^EUQo?Bk}zqR)tF7j_CV{rUvUEFf+3h+0H6A?I_iU*W|yfd`15NV=;o96>^x zwe%qh;Jyz|=tf*#tZ@uZOg`9TMN6(U1WLfLE}kNtt2%mDZPq;i^{Oz&;p29)E*HJf z=oq9#FJ{P##+wx8odsod-FL^_g+{=y_m_G}Q+5J9nWuX_*Z>HtuusB67cEU9V^iC) zC~4|XE9jEhbpx?zuf6gz1gF)CnT-9rGXgy1&Pv>QUR>8i>hi0!l`?s~!MF4Iiv*EeM*|E(Llw@ZL}jt^1i8F0tBS$YGfagzHkm+RyrB|qVKV_k2fTCO zqS7h}LfRZP=1G-H$F7*8zU8T7doo!co(AEm!8}3IO0tJRV0|a!8m?LT^1cZ@kcrzY zemyXsOv}IgzmpO0q@>aW6r{0RhNf69am<&_s>M@&^J z$3RuWuF%u4Q|z1zVfScRJXe6zrzHQJiO*uZQTBUG`cloBRrP33u6C;Bs7-?B90cz? zp$|;~Zxr$ahEGNx?O+P#zc!k3d<0F_T|PTaAG4IL1Kh5t((M9Z6B*IjW!VgEuE;i$ z?Y;nZC5S1diz0;-DX`0c>c}~SF00rH^lc!$WZ}Bd9^0FyZaV?T84;2m)j#~ zjHS530a!W$xpP)3|HfLab=SGK%8?B;CJGMnJ@f(hzbLU(;fn?PB|y!X4tmz4cI}{R zySr>&Z$*tJR492SEmE~gp(X;o%g+{F&@~y(Ei_pxi5@nkr7HH^;S3bzxOI13O&72~ ze$4Z@y?PgWlTaH#fu?2;OB4#AY&MfOn(HjH%uAh7ghx8)uYsUU0H$eG;m0JECkgey zPL&FNYpAIXUM{G7D*MRQoAu-EaE_;g`NN?DXW#$;3YtkoK~zRErzL#44wn|%@@0fd zC-d|y)O+ju7JdiD{bdHpko|!{zV%HlE?gjR__~qipld9%hn4RvKsv$ZnQ~yp*jzVF zw3o`ttO>pCGm|T6GNa}JNDs2nG?)I5`Od~0ZoIemtLgGP4`1oprI+Oqj4ns8Yl&o{ z7w~1~=MBfGsbkX9Ay<}D2d)DHhxFIs3W;?c4PG~xIU$eKd^&O>B_up0RxAP>fo|WG zTY}+C%?5pG6Yu^)drJC2`{~Sp7i&uTHwDJ8WV8R5KX1Oh@GlZi-h7lH(}pxnTl$_T ztqn^a%UjC@f3XQ1bs%cogWhaOO?iShw=x-6Xrh zOLwZLD5dnbV^+TP;i(pzKD1h*rdlw@LVQI+Gmk7+lXevn4#3qj3E06~rG|}Edw3nv zYp9H2mfJ#G`}CR&^-`y>kB+oxKrA1 zpSxi@4Wi@6#S^N`s5rsR=wn+RM2-hMk*$t>dRl|3n| zf!F0m;9LWe$x;@H- z-wKlVz>H?+ifnfQx9g~aZJ~Rz4Gr9@JU!D<g;p9Ydj*zt&eLB!`p?h z8zbJWu0a>H+G8}f{13xh=Emy@dav9Wlhg1m+n$pH%bkxZ_iJAUk(s2YDm>A7OndOb z5pg87!ZJJH>LPgTzy|V|E#kN3H46FJYV;U%buco^SdoFWeaMiWImI&C&gX`rupY%) zDbW-QgY=Kl3U}R7_h#ZXZ&jJ6m0%*;vP?iS>GtT*m{OZ6FT2^!s%s+gZ4z&IHh)!^ zGqLSL0hZ2CdP=IeCwJxqPF`z&#UQ+MAb?OXJNOA*K}v}G6}o@V4b^+cKa(X5+V3-J z+J^;qYbJ)*Z`+oUQ30}d>2wE!iql2wW#i+80-%`6(^q;9lB~#ggHJZQ1l0YN=AdAA zgJnBtxdV|VhRS+kZ{nRl>nSBAh}>P`Ec2(w_BG|YJqQ!XUCQfM$Xr)Y6WcY}kdkFO zYTO`F=E$E>0RVwas$YljfU`x6{Fe4QGD*YT9f8)&*i{5Pm~YE`^))2+6>m$eH7R>y zw71~mkURd0t^B0{nsfzyX8u=B?EH~?zJOGC3h1^;H^CX*+aooB*lXuoYovQZca*;r z#RmDae;)gw2eg$xQxIHK1$mm%a8G5h-kvUtO#`_}zD60QHwVV+#jLp?UjN1r=3<9( zoCXt%uXieajn6p0s95QFugGprVyzkK0=peII#f)>1M-4XZhwa&bV0?wds6lC7f;M@ z2=l|t0|yy%#$SEjQSvJDEUw*)j-s}1Ymx6KQmgpU(N%>?(y_R0*2ped0ar}$Qq9gg zC^0`OmF{XI8^p04zIpA-Kk#OCvmU5fLYj7lz|`^}yrzWnf|~6_V7$g}4_YPAAOl_R z0pnL3-OJ6%kaBYL8RcCUUxGga%)W2@|^Nnmp@`O^C6rb+zc8Q#S&(o2|;<;foq!$4U+C!EKiHfI$KdxAEWndZV``EMG|RPCoFf`Wtv*<5M1#O%DffB<=no) z0*7~um%@$%VHJzp7Jr-if$6I*-4G6)`D4Sq0PPhD;&b8ab%`4>`lj%J7J^?fp$$;0LG4r%v; zXmu?EK{VOPHYBZ6S59jca0u@?aFo%b%p<*iV5T{uG$E4z*7@=BTFOd5)kdg|^7jhQ34Of>Yk|{557syO6%?6Nx ziQ6{gAxZ2TIy&4q;s)0j@Pc*g#?{zaS&Wf|0;o9!f@I74JGKe)UgpDL$#BZg%|@PI z_AzAn>iwxKaeAk?0J!_7F654=U4&*WO1Vab4@d&2zoJeqPvXRJL|ewnnCKs2b%K>; zqL!ZQr^wV6^)44bzehUihRjQzUH8Ps9DpvYUuaIDY)I-xL0`uYYTtvyBP7jZPEtU3 zk#qqd0Tc1VdRV*^N--H4K?u<=4gCTRu3PTkAy&sP9O~j1!npmmqh z!IRKqK#}4_rOn3Q?O}_gyfHfG4&gMtoS}x7n*Kdiy6H@9(H+rRR=ZElQN#X?l5?`Tyax^3UTi6-N-Nf$sy`8E`8H%hwinQ~n2y C|Nlb( From e9939acf4f054aa1f70b9268df09805e958758c9 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 1 May 2018 15:50:01 +0100 Subject: [PATCH 08/37] better enum display "header" just displays enum nick --- ChangeLog | 3 +++ configure.ac | 4 ++-- po/en_GB.gmo | Bin 768 -> 727 bytes src/imageheader.c | 32 ++++++++++++-------------------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index b6a71617..e1da6548 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.6.1 1/5/18 +- better enum display in header + started 8.6.0 16/8/17 - add scRGB support - improve radiance support diff --git a/configure.ac b/configure.ac index 679a3025..85547de7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.6.0], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.6.1], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -17,7 +17,7 @@ dnl m4_define([nip_major_version], [8]) m4_define([nip_minor_version], [6]) -m4_define([nip_micro_version], [0]) +m4_define([nip_micro_version], [1]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/po/en_GB.gmo b/po/en_GB.gmo index 32fea4e6927bc05dd3dc2adf6dd86f1fc2da2305..c6734198824f4d07b5ecbb4c5d251746ca36e903 100644 GIT binary patch delta 60 zcmZo*yUsenN7tT_fk76Cc^DWNVwo5ioPcx|kQM;aYk@QuklqKRd4crFiIsOZ*E2RU F0ssO?3Eltz delta 80 zcmcc4+Q2r!M>m6!fk76Cc^DWN+L#y^oPhLXAT0o-uK{T;ApIOj^8)El6D#kshv+&N Zr6!h4j$xEwH#XKbFf=gO9L(6l2mmEy4}$;z diff --git a/src/imageheader.c b/src/imageheader.c index f0f01113..69e536d3 100644 --- a/src/imageheader.c +++ b/src/imageheader.c @@ -68,26 +68,20 @@ imageheader_add_item( IMAGE *im, char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); GtkTreeIter iter; - char *value_str; - const char *extra; - value_str = g_strdup_value_contents( value ); - vips_buf_appendf( &buf, "%s", value_str ); - - /* Look for enums and decode them. + /* Show the nicks for enums. */ - extra = NULL; - if( strcmp( field, "coding" ) == 0 ) - extra = vips_enum_nick( VIPS_TYPE_CODING, - g_value_get_int( value ) ); - else if( strcmp( field, "format" ) == 0 ) - extra = vips_enum_nick( VIPS_TYPE_BAND_FORMAT, - g_value_get_int( value ) ); - else if( strcmp( field, "interpretation" ) == 0 ) - extra = vips_enum_nick( VIPS_TYPE_INTERPRETATION, - g_value_get_int( value ) ); - if( extra ) - vips_buf_appendf( &buf, " - %s", extra ); + if( G_VALUE_HOLDS_ENUM( value ) ) + vips_buf_appendf( &buf, "%s", + vips_enum_nick( G_VALUE_TYPE( value ), + g_value_get_enum( value ) ) ); + else { + char *value_str; + + value_str = g_strdup_value_contents( value ); + vips_buf_appendf( &buf, "%s", value_str ); + g_free( value_str ); + } gtk_list_store_append( imageheader->store, &iter ); gtk_list_store_set( imageheader->store, &iter, @@ -95,8 +89,6 @@ imageheader_add_item( IMAGE *im, VALUE_COLUMN, vips_buf_all( &buf ), -1 ); - g_free( value_str ); - return( NULL ); } From b780b148f0c3dc5c6902c2cb9eb9c27cc80a3e2f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 16 May 2018 15:07:28 +0100 Subject: [PATCH 09/37] extra includes for libvips 8.7 add the extra `vips7compat.h` we will need with libvips 8.7 --- src/doubleclick.c | 1 + src/ip.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/doubleclick.c b/src/doubleclick.c index 13df069d..75196876 100644 --- a/src/doubleclick.c +++ b/src/doubleclick.c @@ -40,6 +40,7 @@ #include #include +#include #include #include "doubleclick.h" diff --git a/src/ip.h b/src/ip.h index 81852a23..7be96bd1 100644 --- a/src/ip.h +++ b/src/ip.h @@ -157,6 +157,7 @@ extern int statfs(); #endif /*HAVE_LIBGVC*/ #include +#include #include #include From 2eae8916bdf2f09195ccbc2e8981ab68fb839f45 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 22 May 2018 09:54:33 +0100 Subject: [PATCH 10/37] bump to 8.7 fix build with libvips8.7 changes ... you unow need vips7compat.h to build with the vips7 API --- ChangeLog | 3 +++ configure.ac | 6 +++--- src/doubleclick.c | 1 + src/ip.h | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index e1da6548..35ee6086 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.7.0 22/5/18 +- added vips7compat.h include for libvips 8.7 + started 8.6.1 1/5/18 - better enum display in header diff --git a/configure.ac b/configure.ac index 85547de7..1e8fff70 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.6.1], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.7.0], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -16,8 +16,8 @@ dnl of them. dnl m4_define([nip_major_version], [8]) -m4_define([nip_minor_version], [6]) -m4_define([nip_micro_version], [1]) +m4_define([nip_minor_version], [7]) +m4_define([nip_micro_version], [0]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/src/doubleclick.c b/src/doubleclick.c index 13df069d..75196876 100644 --- a/src/doubleclick.c +++ b/src/doubleclick.c @@ -40,6 +40,7 @@ #include #include +#include #include #include "doubleclick.h" diff --git a/src/ip.h b/src/ip.h index 81852a23..7be96bd1 100644 --- a/src/ip.h +++ b/src/ip.h @@ -157,6 +157,7 @@ extern int statfs(); #endif /*HAVE_LIBGVC*/ #include +#include #include #include From 1b1b27f6b61096f0230b9d7db2d2d361fa4758ed Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 10 Jun 2018 16:12:38 +0100 Subject: [PATCH 11/37] more -V output and some small cleanups --- ChangeLog | 1 + nip2.appdata.xml | 4 ++-- nip2.spec.in | 10 +++++----- src/ip.h | 10 ++++++---- src/main.c | 15 ++++++++------- src/main.h | 1 + 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 35ee6086..37bfa0e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ started 8.7.0 22/5/18 - added vips7compat.h include for libvips 8.7 +- more output for -V to help debugging CLI mode started 8.6.1 1/5/18 - better enum display in header diff --git a/nip2.appdata.xml b/nip2.appdata.xml index 22736a9b..6d112d4f 100644 --- a/nip2.appdata.xml +++ b/nip2.appdata.xml @@ -22,8 +22,8 @@

- http://www.vips.ecs.soton.ac.uk/images/thumb/Nip2-7.36.png/800px-Nip2-7.36.png + https://raw.githubusercontent.com/jcupitt/nip2/master/doc/src/figs/snap1.jpg - http://www.vips.ecs.soton.ac.uk + https://jcupitt.github.io/libvips https://github.com/jcupitt/nip2 diff --git a/nip2.spec.in b/nip2.spec.in index 2511364a..c35132b5 100644 --- a/nip2.spec.in +++ b/nip2.spec.in @@ -5,8 +5,8 @@ Summary: Interactive tool for working with large images Group: Applications/Multimedia License: GPLv2+ -URL: http://www.vips.ecs.soton.ac.uk/ -Source0: http://www.vips.ecs.soton.ac.uk/supported/7.14/%{name}-%{version}.tar.gz +URL: https://github.com/jcupitt/nip2 +Source0: https://github.com/jcupitt/nip2/releases BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -33,9 +33,6 @@ GIMP should be used instead. %prep %setup -q -# CVS dirs -find . -name CVS -type d -print0 | xargs -0 rm -rf - %build %configure @@ -120,6 +117,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Sat Jun 10 2008 John Cupitt - 8.7.0 +- Update URLs + * Sat Jul 19 2008 Jesper Friis - 7.15.0-1 - Added this spec file from the Fedora source rpm diff --git a/src/ip.h b/src/ip.h index 7be96bd1..fcb06b56 100644 --- a/src/ip.h +++ b/src/ip.h @@ -180,6 +180,10 @@ extern int statfs(); */ #include "nipmarshal.h" +/* XML namespace ... note, not nip2! We can't change this. + */ +#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/nip" + #define MAXFILES (4000) /* Max. no of files in path */ #define STACK_SIZE (1000) /* Depth of eval stack */ #define LEN_LABEL (512) /* Label on windows */ @@ -189,18 +193,16 @@ extern int statfs(); #define MAX_STRSIZE (100000) /* Size of text for user defs */ #define MAX_TRACE (1024) /* Biggest thing we print in trace */ #define MAX_SSTACK (40) /* Scope stack for parser */ -#define VIPS_HOMEPAGE "http://www.vips.ecs.soton.ac.uk" +#define VIPS_HOMEPAGE "https://github.com/jcupitt/nip2" #define IP_NAME PACKAGE "-" VERSION #define NIP_DOCPATH "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S \ "doc" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "html" #define VIPS_DOCPATH "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S \ "doc" G_DIR_SEPARATOR_S "vips" G_DIR_SEPARATOR_S "html" #define IP_NAME PACKAGE "-" VERSION -#define NAMESPACE VIPS_HOMEPAGE "/" "nip" - /* XML namespace ... note, not nip2! */ #define MAX_LINELENGTH (120) /* Max chars we display of value */ #define MAX_RECENT (10) /* Number of recent items in file menu */ -#define NIP_COPYRIGHT "%s: ©2017 Imperial College, London" +#define NIP_COPYRIGHT "%s: ©2018 Imperial College, London" /* Our stock_ids. */ diff --git a/src/main.c b/src/main.c index 411148fd..aee51221 100644 --- a/src/main.c +++ b/src/main.c @@ -94,9 +94,9 @@ static gboolean main_option_benchmark = FALSE; gboolean main_option_time_save = FALSE; gboolean main_option_profile = FALSE; gboolean main_option_i18n = FALSE; +gboolean main_option_verbose = FALSE; static gboolean main_option_print_main = FALSE; static gboolean main_option_version = FALSE; -static gboolean main_option_verbose = FALSE; static gboolean main_option_test = FALSE; static char *main_option_prefix = NULL; @@ -908,10 +908,6 @@ main_set( const char *str ) { Symbol *sym; -#ifdef DEBUG - printf( "main_set: %s\n", str ); -#endif /*DEBUG*/ - attach_input_string( str ); if( !(sym = parse_set_symbol()) ) return( FALSE ); @@ -1485,9 +1481,14 @@ main( int argc, char *argv[] ) if( main_option_set ) { int i; - for( i = 0; main_option_set[i]; i++ ) + for( i = 0; main_option_set[i]; i++ ) { + if( main_option_verbose ) + printf( "main_set: %s\n", main_option_set[i] ); + if( !main_set( main_option_set[i] ) ) - main_log_add( "%s\n", error_get_sub() ); + main_log_add( "%s\n%s", + error_get_top(), error_get_sub() ); + } } /* Make sure our start ws doesn't have modified set. We may have diff --git a/src/main.h b/src/main.h index 683265d3..4a5b6cb1 100644 --- a/src/main.h +++ b/src/main.h @@ -41,6 +41,7 @@ extern gboolean main_option_time_save; /* Time save image ops */ extern gboolean main_option_profile; /* Profile calcualtion */ extern gboolean main_option_i18n; /* Output i18n strings */ extern gboolean main_option_batch; /* Running in batch mode */ +extern gboolean main_option_verbose; /* Verbose output */ /* Styles for buttons etc. */ From 4b1503db82102acab2ac2ef4f5e94cc6dc24e15b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 31 Jul 2018 15:36:27 +0100 Subject: [PATCH 12/37] start updating menus for 8.7 --- share/nip2/start/Filter.def | 81 ++++++++++++++++++++---------------- share/nip2/start/Image.def | 28 +++++++++++-- share/nip2/start/_stdenv.def | 35 ++++++++++++++++ 3 files changed, 105 insertions(+), 39 deletions(-) diff --git a/share/nip2/start/Filter.def b/share/nip2/start/Filter.def index a8ac20c0..15beddd7 100644 --- a/share/nip2/start/Filter.def +++ b/share/nip2/start/Filter.def @@ -20,6 +20,41 @@ Filter_conv_item = class action x = map_unary (conv filter_sharp) x; } + Usharp_item = class + Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { + action x = class + _result { + _vislevel = 3; + + size = Option "Radius" [ + "3 pixels", + "5 pixels", + "7 pixels", + "9 pixels", + "11 pixels", + "51 pixels" + ] 0; + + st = Scale "Smoothness threshold" 0 5 2; + bm = Scale "Brighten by at most" 1 50 10; + dm = Scale "Darken by at most" 1 50 20; + fs = Scale "Sharpen flat areas by" 0 5 0.5; + js = Scale "Sharpen jaggy areas by" 0 5 1; + + _result + = map_unary process x + { + process in + = Image in''' + { + in' = colour_transform_to Image_type.LABS in.value; + in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; + in''' = colour_transform_to (get_type in) in''; + } + } + } + } + Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; @@ -32,16 +67,7 @@ Filter_conv_item = class Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { - action x - = map_unary sobel x - { - sobel im - = abs (a - 128) + abs (b - 128) - { - a = conv filter_sobel im; - b = conv (rot270 filter_sobel) im; - } - } + action x = map_unary sobel x; } /* 3x3 line detect of image @@ -62,37 +88,20 @@ Kirk } } - Usharp_item = class - Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { - action x = class + Canny_item = class + Menuaction "Canny" "Canny edge detector" { + action x = class _result { _vislevel = 3; - - size = Option "Radius" [ - "3 pixels", - "5 pixels", - "7 pixels", - "9 pixels", - "11 pixels", - "51 pixels" - ] 0; - - st = Scale "Smoothness threshold" 0 5 2; - bm = Scale "Brighten by at most" 1 50 10; - dm = Scale "Darken by at most" 1 50 20; - fs = Scale "Sharpen flat areas by" 0 5 0.5; - js = Scale "Sharpen jaggy areas by" 0 5 1; - - _result + + sigma = Scale "Sigma" 0.1 10 2; + prec = Option "Precision" ["Int", "Float", "Approximate"] 1; + + _result = map_unary process x { process in - = Image in''' - { - in' = colour_transform_to Image_type.LABS in.value; - in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; - in''' = colour_transform_to (get_type in) in''; - } + = canny sigma prec.value in; } } } diff --git a/share/nip2/start/Image.def b/share/nip2/start/Image.def index a010d989..645e0c2d 100644 --- a/share/nip2/start/Image.def +++ b/share/nip2/start/Image.def @@ -1902,16 +1902,38 @@ Pattern_images_item = class text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; - wrap = Expression "Wrap text at" 500; + fontfile = Pathname "Font file" ""; + width = Expression "Text width" 500; + height = Expression "Text height" 0; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; + spacing = Expression "Line spacing" 1.1; + + _result + = Image out + { + [out] = vips_call "text" [text.value] + (base_options ++ fontfile_options); + + base_options = [ + $font => font.value, + $width => to_real width, + $height => to_real height, + $align => align.value, + $dpi => to_real dpi, + $spacing => to_real spacing + ]; + + fontfile_options + = [$fontfile => fontfile.value], + fontfile.value != "" + = []; + } - _result = Image (im_text text.value font.value - (to_real wrap) align.value (to_real dpi)); } } diff --git a/share/nip2/start/_stdenv.def b/share/nip2/start/_stdenv.def index 0112d1f7..350cd709 100644 --- a/share/nip2/start/_stdenv.def +++ b/share/nip2/start/_stdenv.def @@ -2617,3 +2617,38 @@ hist_entropy x ]; } } + +canny sigma precision x + = oo_unary_function canny_op x, is_class x + = canny_im (to_real sigma) (to_int precision) x, is_image x + = error (_ "bad arguments to " ++ "canny") +{ + canny_op = Operator "canny" + (canny sigma precision) Operator_type.COMPOUND_REWRAP false; + + canny_im sigma precision x + = out + { + [out] = vips_call "canny" [x] [ + $sigma => sigma, + $precision => precision + ]; + } + +} + +sobel x + = oo_unary_function sobel_op x, is_class x + = sobel_im x, is_image x + = error (_ "bad arguments to " ++ "sobel") +{ + sobel_op = Operator "sobel" + sobel Operator_type.COMPOUND_REWRAP false; + + sobel_im x + = out + { + [out] = vips_call "sobel" [x] [ + ]; + } +} From 29eb9f28864ec3f5fac6c9ece0deac58d675ad2d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 1 Aug 2018 13:58:37 +0100 Subject: [PATCH 13/37] add new text controls --- share/nip2/start/Image.def | 54 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/share/nip2/start/Image.def b/share/nip2/start/Image.def index 645e0c2d..962dbd18 100644 --- a/share/nip2/start/Image.def +++ b/share/nip2/start/Image.def @@ -1903,7 +1903,7 @@ Pattern_images_item = class text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; fontfile = Pathname "Font file" ""; - width = Expression "Text width" 500; + width = Expression "Text width" 0; height = Expression "Text height" 0; align = Option "Alignment" [ "Left", @@ -1911,29 +1911,31 @@ Pattern_images_item = class "Right" ] 0; dpi = Expression "DPI" 300; - spacing = Expression "Line spacing" 1.1; + fit = Toggle "Fit text to box" false; + spacing = Expression "Line spacing" 0; _result = Image out { - [out] = vips_call "text" [text.value] - (base_options ++ fontfile_options); - base_options = [ $font => font.value, - $width => to_real width, - $height => to_real height, - $align => align.value, - $dpi => to_real dpi, - $spacing => to_real spacing + $align => align.value ]; - - fontfile_options - = [$fontfile => fontfile.value], - fontfile.value != "" + + set_option name default value + = [name => value], value != default = []; - } + options = base_options ++ concat [ + set_option $width 0 (to_real width), + set_option $height 0 (to_real height), + set_option $fontfile "" fontfile.value, + if !fit then set_option $dpi 72 (to_real dpi) else [], + set_option $spacing 0 (to_real spacing) + ]; + + [out] = vips_call "text" [text.value] options; + } } } @@ -1977,7 +1979,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f sense.value fc.value 0 0 0 0; + param f = f sense.value fc.value 0 0 0 0; } } } @@ -1998,7 +2000,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 6) fc.value rw.value 0 0 0; + param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } @@ -2020,7 +2022,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 12) fcx.value fcy.value + param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } @@ -2046,7 +2048,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 4) fc.value ac.value 0 0 0; + param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } @@ -2068,7 +2070,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 10) fc.value rw.value + param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } @@ -2092,7 +2094,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 16) fcx.value fcy.value + param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } @@ -2119,7 +2121,7 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 2) order.value fc.value + param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } @@ -2143,8 +2145,8 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 8) order.value fc.value - rw.value ac.value 0; + param f = f (sense.value + 8) order.value fc.value + rw.value ac.value 0; } } } @@ -2168,8 +2170,8 @@ Pattern_images_item = class _result = build param (to_real nsize) { - param f = f (sense.value + 14) order.value fcx.value - fcy.value r.value ac.value; + param f = f (sense.value + 14) order.value fcx.value + fcy.value r.value ac.value; } } } From 332a300dee7a247548daac8ce9c2ff31b0d72585 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 1 Aug 2018 14:20:38 +0100 Subject: [PATCH 14/37] add 8.6 compat and mitchell --- ChangeLog | 5 + configure.ac | 1 + share/nip2/compat/8.6/Colour.def | 690 ++++++ share/nip2/compat/8.6/Filter.def | 1694 ++++++++++++++ share/nip2/compat/8.6/Histogram.def | 341 +++ share/nip2/compat/8.6/Image.def | 2329 +++++++++++++++++++ share/nip2/compat/8.6/Magick.def | 2423 ++++++++++++++++++++ share/nip2/compat/8.6/Makefile.am | 26 + share/nip2/compat/8.6/Math.def | 588 +++++ share/nip2/compat/8.6/Matrix.def | 537 +++++ share/nip2/compat/8.6/Object.def | 49 + share/nip2/compat/8.6/Preferences.ws | 919 ++++++++ share/nip2/compat/8.6/Tasks.def | 952 ++++++++ share/nip2/compat/8.6/Widgets.def | 46 + share/nip2/compat/8.6/_Object.def | 364 +++ share/nip2/compat/8.6/_convert.def | 739 ++++++ share/nip2/compat/8.6/_generate.def | 155 ++ share/nip2/compat/8.6/_joe_extra.def | 505 +++++ share/nip2/compat/8.6/_joe_utilities.def | 705 ++++++ share/nip2/compat/8.6/_list.def | 482 ++++ share/nip2/compat/8.6/_magick.def | 1107 +++++++++ share/nip2/compat/8.6/_predicate.def | 530 +++++ share/nip2/compat/8.6/_stdenv.def | 2619 ++++++++++++++++++++++ share/nip2/compat/8.6/_types.def | 1410 ++++++++++++ share/nip2/compat/Makefile.am | 3 +- share/nip2/start/Preferences.ws | 2 +- share/nip2/start/_types.def | 7 +- 27 files changed, 19224 insertions(+), 4 deletions(-) create mode 100644 share/nip2/compat/8.6/Colour.def create mode 100644 share/nip2/compat/8.6/Filter.def create mode 100644 share/nip2/compat/8.6/Histogram.def create mode 100644 share/nip2/compat/8.6/Image.def create mode 100644 share/nip2/compat/8.6/Magick.def create mode 100644 share/nip2/compat/8.6/Makefile.am create mode 100644 share/nip2/compat/8.6/Math.def create mode 100644 share/nip2/compat/8.6/Matrix.def create mode 100644 share/nip2/compat/8.6/Object.def create mode 100644 share/nip2/compat/8.6/Preferences.ws create mode 100644 share/nip2/compat/8.6/Tasks.def create mode 100644 share/nip2/compat/8.6/Widgets.def create mode 100644 share/nip2/compat/8.6/_Object.def create mode 100644 share/nip2/compat/8.6/_convert.def create mode 100644 share/nip2/compat/8.6/_generate.def create mode 100644 share/nip2/compat/8.6/_joe_extra.def create mode 100644 share/nip2/compat/8.6/_joe_utilities.def create mode 100644 share/nip2/compat/8.6/_list.def create mode 100644 share/nip2/compat/8.6/_magick.def create mode 100644 share/nip2/compat/8.6/_predicate.def create mode 100644 share/nip2/compat/8.6/_stdenv.def create mode 100644 share/nip2/compat/8.6/_types.def diff --git a/ChangeLog b/ChangeLog index 37bfa0e5..cf0964b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ started 8.7.0 22/5/18 - added vips7compat.h include for libvips 8.7 - more output for -V to help debugging CLI mode +- revised Text widget +- added Canny +- Sobel uses the new vips_sobel() operator +- add mitchell kernel +- add 8.6 compat started 8.6.1 1/5/18 - better enum display in header diff --git a/configure.ac b/configure.ac index 1e8fff70..306a634a 100644 --- a/configure.ac +++ b/configure.ac @@ -381,6 +381,7 @@ AC_OUTPUT([ share/nip2/compat/8.3/Makefile share/nip2/compat/8.4/Makefile share/nip2/compat/8.5/Makefile + share/nip2/compat/8.6/Makefile src/BITMAPS/Makefile src/Makefile test/Makefile diff --git a/share/nip2/compat/8.6/Colour.def b/share/nip2/compat/8.6/Colour.def new file mode 100644 index 00000000..6d042103 --- /dev/null +++ b/share/nip2/compat/8.6/Colour.def @@ -0,0 +1,690 @@ + +Colour_new_item = class + Menupullright (_ "_New") (_ "make a patch of colour") { + Widget_colour_item = class + Menuaction (_ "_Colour") (_ "make a patch of colour") { + action = Colour_picker "Lab" [50,0,0]; + } + + LAB_colour = class + Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { + action = widget "Lab" [50, 0, 0]; + + // ab_slice size + size = 512; + + // range of values ... +/- 128 for ab + range = 256; + + // map xy in slice image to ab and back + xy2ab x = x / (size / range) - 128; + ab2xy a = (a + 128) * (size / range); + + widget space default_value = class + Colour space _result { + _vislevel = 3; + + [_L, _a, _b] = default_value; + L = Scale "Lightness" 0 100 _L; + ab_slice = Image (lab_slice size L.value); + point = Mark ab_slice (ab2xy _a) (ab2xy _b); + + _result = [L.value, xy2ab point.left, xy2ab point.top]; + + Colour_edit colour_space value = widget colour_space value; + } + } + + CCT_colour = class + Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { + action = widget 6500; + + widget x = class + _result { + _vislevel = 3; + + T = Scale "CCT" 1800 25000 x; + + _result = colour_from_temp (to_real T); + + Colour_edit space value + = widget (temp_from_colour (Colour space value)); + } + } +} + +Colour_to_colour_item = class + Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { + action x = to_colour x; +} + +#separator + +Colour_convert_item = class + Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { + spaces = Image_type.image_colour_spaces; + + conv dest x = class + _result { + _vislevel = 3; + + to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); + + _result = map_unary (colour_transform_to to.value_thing) x; + } + + Mono_item = class + Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { + action x = conv Image_type.B_W x; + } + + sRGB_item = class + Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { + action x = conv Image_type.sRGB x; + } + + scRGB_item = class + Menuaction (_ "_scRGB") (_ "convert to scRGB colourspace") { + action x = conv Image_type.scRGB x; + } + + GREY16_item = class + Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { + action x = conv Image_type.GREY16 x; + } + + RGB16_item = class + Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { + action x = conv Image_type.RGB16 x; + } + + Lab_item = class + Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { + action x = conv Image_type.LAB x; + } + + LabQ_item = class + Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { + action x = conv Image_type.LABQ x; + } + + LabS_item = class + Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { + action x = conv Image_type.LABS x; + } + + LCh_item = class + Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { + action x = conv Image_type.LCH x; + } + + XYZ_item = class + Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { + action x = conv Image_type.XYZ x; + } + + Yxy_item = class + Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { + action x = conv Image_type.YXY x; + } + + UCS_item = class + Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { + action x = conv Image_type.UCS x; + } +} + +/* mark objects as being in various colourspaces + */ +Colour_tag_item = class + Menupullright (_ "_Tag As") + (_ "tag object as being in various colour spaces") { + spaces = Image_type.image_colour_spaces; + + tag dest x = class + _result { + _vislevel = 3; + + to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); + + _result = map_unary (image_set_type to.value_thing) x; + } + + Mono_item = class + Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { + action x = tag Image_type.B_W x; + } + + sRGB_item = class + Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { + action x = tag Image_type.sRGB x; + } + + scRGB_item = class + Menuaction (_ "_scRGB") (_ "tag as being in scRGB colourspace") { + action x = tag Image_type.scRGB x; + } + + RGB16_item = class + Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { + action x = tag Image_type.RGB16 x; + } + + GREY16_item = class + Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { + action x = tag Image_type.GREY16 x; + } + + Lab_item = class + Menuaction (_ "_Lab") + (_ "tag as being in Lab colourspace (float Lab)") { + action x = tag Image_type.LAB x; + } + + LabQ_item = class + Menuaction (_ "Lab_Q") + (_ "tag as being in LabQ colourspace (32-bit Lab)") { + action x = tag Image_type.LABQ x; + } + + LabS_item = class + Menuaction (_ "Lab_S") + (_ "tag as being in LabS colourspace (48-bit Lab)") { + action x = tag Image_type.LABS x; + } + + LCh_item = class + Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { + action x = tag Image_type.LCH x; + } + + XYZ_item = class + Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { + action x = tag Image_type.XYZ x; + } + + Yxy_item = class + Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { + action x = tag Image_type.YXY x; + } + + UCS_item = class + Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { + action x = tag Image_type.UCS x; + } +} + +Colour_temperature_item = class + Menupullright (_ "Te_mperature") + (_ "colour temperature conversions") { + Whitepoint_item = class + Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { + action x = class + _result { + _vislevel = 3; + + old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; + new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; + + _result + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im' * + (new_white.value_thing / old_white.value_thing); + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + D65_to_D50_item = class + Menupullright (_ "D_65 to D50") (_ "complex conversion") { + XYZ_minimal_item = class + Menuaction (_ "_Minimal") + (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = recomb D652D50_direct im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + + Bradford_item = class + Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im_D652D50 im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + D50_to_D65_item = class + Menupullright (_ "D_50 to D65") (_ "complex conversion") { + XYZ_minimal_item = class + Menuaction (_ "_Minimal") + (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = recomb D502D65_direct im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + + Bradford_item = class + Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { + action x + = map_unary process x + { + process im + = im''' + { + im' = colour_transform_to Image_type.XYZ im; + im'' = im_D502D65 im'; + im''' = colour_transform_to (get_type im) im''; + } + } + } + } + + Lab_to_D50XYZ_item = class + Menuaction (_ "_Lab to D50 XYZ") + (_ "Lab to XYZ with a D50 whitepoint") { + action x = map_unary (colour_unary im_D50Lab2XYZ) x; + } + + D50XYZ_to_Lab_item = class + Menuaction (_ "D50 _XYZ to Lab") + (_ "XYZ to Lab with a D50 whitepoint") { + action x = map_unary (colour_unary im_D50XYZ2Lab) x; + } + + sep1 = Menuseparator; + + CCT_item = class + Menuaction (_ "Calculate temperature") + (_ "estimate CCT using the McCamy approximation") { + action z = map_unary temp_from_colour z; + } + + Colour_item = Colour_new_item.CCT_colour; +} + +Colour_icc_item = class + Menupullright (_ "_ICC") (_ "transform with ICC profiles") { + print_profile = + "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; + monitor_profile = + "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; + guess_profile image + = print_profile, + has_type image && + get_type image == Image_type.CMYK && + has_bands image && + get_bands image >= 4 + = monitor_profile; + render_intents = Option_enum (_ "Render intent") Render_intent.names + (_ "Absolute"); + + Export_item = class + Menuaction (_ "_Export") (_ "export from PCS to device space") { + action x = class + _result { + _vislevel = 3; + + profile = Pathname (_ "Output profile") print_profile; + intent = render_intents; + depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; + + _result + = map_unary process x + { + process image + = icc_export [8, 16]?depth profile.value + intent.value_thing lab + { + lab = colour_transform_to Image_type.LABQ image; + } + } + } + } + + Import_item = class + Menuaction (_ "_Import") (_ "import from device space to PCS") { + action x = class + _result { + _vislevel = 3; + + embedded = Toggle (_ "Use embedded profile if possible") false; + profile = Pathname (_ "Default input profile") (guess_profile x); + intent = render_intents; + + _result + = map_unary process x + { + process image + = icc_import_embedded intent.value_thing image, + get_header_type "icc-profile-data" image != 0 && + embedded + = icc_import profile.value intent.value_thing image; + } + } + } + + Transform_item = class + Menuaction (_ "_Transform") (_ "transform between two device spaces") { + action x = class + _result { + _vislevel = 3; + + in_profile = Pathname (_ "Input profile") (guess_profile x); + out_profile = Pathname (_ "Output profile") print_profile; + intent = render_intents; + + _result + = map_unary process x + { + process image + = icc_transform in_profile.value out_profile.value + intent.value_thing image; + } + } + } + + AC2RC_item = class + Menuaction (_ "_Absolute to Relative") + (_ "absolute to relative colorimetry using device profile") { + action x = class + _result { + _vislevel = 3; + + profile = Pathname (_ "Pick a profile") (guess_profile x); + + _result + = map_unary process x + { + process image + = icc_ac2rc profile.value lab + { + lab = colour_transform_to Image_type.LAB image; + } + } + } + } +} + +Colour_rad_item = class + Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { + Unpack_item = class + Menuaction (_ "Unpack") + (_ "unpack Radiance format to float") { + action x = map_unary rad2float x; + } + + Pack_item = class + Menuaction (_ "Pack") + (_ "pack 3-band float to Radiance format") { + action x = map_unary float2rad x; + } +} + +#separator + +Colour_dE_item = class + Menupullright (_ "_Difference") (_ "calculate colour difference") { + /* Apply a converter to an object ... convert image or colour (since + * we can guess the colour space we're converting from), don't convert + * matrix or vector (since we can't tell ... assume it's in the right + * space already). + */ + apply_cvt cvt x + = cvt x, + is_Image x || is_Colour x || is_image x + = x; + + diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); + + /* Converter to LAB. + */ + lab_cvt = colour_transform_to Image_type.LAB; + + /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after + * to make sure we get a rectangular coord system. + */ + ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ + colour_transform_to Image_type.UCS; + + CIEdE76_item = class + Menuaction (_ "CIE dE _76") + (_ "calculate CIE dE 1976 for two objects") { + action a b = map_binary (diff lab_cvt) a b; + } + + CIEdE00_item = class + Menuaction (_ "CIE dE _00") + (_ "calculate CIE dE 2000 for two objects") { + action a b = map_binary + (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; + } + + UCS_item = class + Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { + action a b = map_binary (diff ucs_cvt) a b; + } +} + +Colour_adjust_item = class + Menupullright (_ "_Adjust") (_ "alter colours in various ways") { + Recombination_item = class + Menuaction (_ "_Recombination") + (_ "recombine colour with an editable matrix") { + action x = class + _result { + _vislevel = 3; + + matrix + = Matrix_rec (identity_matrix (bands x)) + { + // try to guess a sensible value for the size of the + // matrix + bands x + = x.bands, is_Image x || is_Colour x + = x.width, is_Matrix x + = bands x.value?0, is_Group x + = x.bands, has_member "bands" x + = 3; + } + + _result = map_unary (recomb matrix) x; + } + } + + Cast_item = class + Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { + action x = class + _result { + _vislevel = 3; + + gr = Scale "Green-red" (-20) 20 0; + by = Scale "Blue-yellow" (-20) 20 0; + + _result + = map_unary adjust_cast x + { + adjust_cast in + = colour_transform_to (get_type in) in'' + { + in' = colour_transform_to Image_type.LAB in; + in'' = in' + + Vector [0, gr.value, by.value]; + } + } + } + } + + HSB_item = class + Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { + action x = class + _result { + _vislevel = 3; + + h = Scale "Hue" 0 360 0; + s = Scale "Saturation" 0.01 5 1; + b = Scale "Brightness" 0.01 5 1; + + _result + = map_unary adjust_hsb x + { + adjust_hsb in + = colour_transform_to (get_type in) in'' + { + in' = colour_transform_to Image_type.LCH in; + in'' = in' * Vector [b.value, s.value, 1] + + Vector [0, 0, h.value]; + } + } + } + } +} + +Colour_similar_item = class + Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { + action x = class + _result { + _vislevel = 3; + + target_colour = Colour_picker "Lab" [50, 0, 0]; + t = Scale "dE threshold" 0 100 10; + + _result + = map_unary match x + { + match in + = abs_vec (in' - target) < t + { + target = colour_transform_to Image_type.LAB target_colour; + in' = colour_transform_to Image_type.LAB in; + } + } + } +} + +#separator + +Colour_chart_to_matrix_item = class + Menuaction (_ "_Measure Colour Chart") + (_ "measure average pixel values for a colour chart image") { + action x = class + _result { + _vislevel = 3; + + pacross = Expression (_ "Patches across chart") 6; + pdown = Expression (_ "Patches down chart") 4; + measure = Scale (_ "Measure area (%)") 1 100 50; + + // get a representative image from an arg + get_image x + = get_image x.value?0, is_Group x + = x; + + _im = get_image x; + sample = measure_draw (to_real pacross) (to_real pdown) + (to_real measure) _im; + + _result + = map_unary chart x + { + chart in + = measure_sample (to_real pacross) (to_real pdown) + (to_real measure) in; + } + } +} + +Colour_matrix_to_chart_item = class + Menuaction (_ "Make Synth_etic Colour Chart") + (_ "make a colour chart image from a matrix of measurements") { + action x = class + _result { + _vislevel = 3; + + pacross = Expression (_ "Patches across chart") 6; + pdown = Expression (_ "Patches down chart") 4; + pwidth = Expression (_ "Patch width in pixels") 50; + pheight = Expression (_ "Patch height in pixels") 50; + bwidth = Expression (_ "Border between patches") 0; + + _result + = map_unary build_chart x + { + build_chart in + = Image (imagearray_assemble + (to_real bwidth) (to_real bwidth) patch_table) + { + // patch numbers for row starts + rowstart = map (multiply (to_real pacross)) + [0 .. to_real pdown - 1]; + + // assemble patches ... each one a pixel value + patches = map (take (to_real pacross)) + (map (converse drop in.value) rowstart); + + // make an n-band constant image from eg. [1,2,3] + // we don't know the format .. use sRGB (well, why not?) + patch v = image_new (to_real pwidth) (to_real pheight) (len v) + Image_format.FLOAT Image_coding.NOCODING + Image_type.sRGB (Vector v) 0 0; + + // make an image for each patch + patch_table = map (map patch) patches; + } + } + } +} + +Colour_plot_ab_scatter_item = class + Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { + action x = class + _result { + _vislevel = 3; + + bins = Expression (_ "Number of bins on each axis") 8; + + _result + = map_unary plot_scatter x + { + plot_scatter in + = Image (bg * (((90 / mx) * hist) ++ blk)) + { + lab = colour_transform_to Image_type.LAB in.value; + ab = (unsigned char) ((lab?1 ++ lab?2) + 128); + hist = hist_find_nD bins.expr ab; + mx = max hist; + bg = lab_slice bins.expr 1; + blk = 1 + im_black (to_real bins) (to_real bins) 2; + } + } + } +} diff --git a/share/nip2/compat/8.6/Filter.def b/share/nip2/compat/8.6/Filter.def new file mode 100644 index 00000000..a8ac20c0 --- /dev/null +++ b/share/nip2/compat/8.6/Filter.def @@ -0,0 +1,1694 @@ +Filter_conv_item = class + Menupullright "_Convolution" "various spatial convolution filters" { + /* Some useful masks. + */ + filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; + filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; + filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; + filter_laplacian = Matrix_con 1 128 + [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; + filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; + filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; + + Blur_item = class + Menuaction "_Blur" "3x3 blur of image" { + action x = map_unary (conv filter_blur) x; + } + + Sharpen_item = class + Menuaction "_Sharpen" "3x3 sharpen of image" { + action x = map_unary (conv filter_sharp) x; + } + + Emboss_item = class + Menuaction "_Emboss" "1 pixel displace emboss" { + action x = map_unary (conv filter_emboss) x; + } + + Laplacian_item = class + Menuaction "_Laplacian" "3x3 laplacian edge detect" { + action x = map_unary (conv filter_laplacian) x; + } + + Sobel_item = class + Menuaction "So_bel" "3x3 Sobel edge detect" { + action x + = map_unary sobel x + { + sobel im + = abs (a - 128) + abs (b - 128) + { + a = conv filter_sobel im; + b = conv (rot270 filter_sobel) im; + } + } + } + +/* 3x3 line detect of image +diagonals should be scaled down by root(2) I guess +Kirk +*/ + Linedet_item = class + Menuaction "Li_ne Detect" "3x3 line detect" { + action x + = map_unary lindet x + { + lindet im + = foldr1 max_pair images + { + masks = take 4 (iterate rot45 filter_lindet); + images = map (converse conv im) masks; + } + } + } + + Usharp_item = class + Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { + action x = class + _result { + _vislevel = 3; + + size = Option "Radius" [ + "3 pixels", + "5 pixels", + "7 pixels", + "9 pixels", + "11 pixels", + "51 pixels" + ] 0; + + st = Scale "Smoothness threshold" 0 5 2; + bm = Scale "Brighten by at most" 1 50 10; + dm = Scale "Darken by at most" 1 50 20; + fs = Scale "Sharpen flat areas by" 0 5 0.5; + js = Scale "Sharpen jaggy areas by" 0 5 1; + + _result + = map_unary process x + { + process in + = Image in''' + { + in' = colour_transform_to Image_type.LABS in.value; + in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; + in''' = colour_transform_to (get_type in) in''; + } + } + } + } + + sep1 = Menuseparator; + + Custom_blur_item = class + Menuaction "Custom B_lur / Sharpen" + "blur or sharpen with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + type = Option "Type" ["Blur", "Sharpen"] 0; + r = Scale "Radius" 1 100 1; + fac = Scale "Amount" 0 1 1; + layers = Scale "Layers" 1 100 10; + shape = Option "Mask shape" [ + "Square", + "Gaussian" + ] 0; + prec = Option "Precision" ["Int", "Float", "Approximate"] 0; + + _result + = map_unary process x + { + process in + = clip2fmt blur.format proc + { + mask + = matrix_blur r.value, shape.value == 0 + = matrix_gaussian_blur r.value; + blur = [convsep, convsepf, aconvsep layers]?prec mask in; + proc + = in + fac * (in - blur), type == 1 + = blur * fac + in * (1 - fac); + } + } + } + } + + Custom_conv_item = class + Menuaction "Custom C_onvolution" + "convolution filter with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; + separable + = Toggle "Seperable convolution" false, + matrix.width == 1 || matrix.height == 1 + = false; + type = Option "Convolution type" ["Int", "Float"] 0; + rotate = Option "Rotate" [ + "Don't rotate", + "4 x 45 degrees", + "8 x 45 degrees", + "2 x 90 degrees" + ] 0; + + _result + = map_unary process x + { + process in + = in.Image in' + { + conv_fn + = im_lindetect, !separable && type == 0 && rotate == 1 + = im_compass, !separable && type == 0 && rotate == 2 + = im_gradient, !separable && type == 0 && rotate == 3 + = im_conv, !separable && type == 0 + = im_convsep, separable && type == 0 + = im_conv_f, !separable && type == 1 + = im_convsep_f, separable && type == 1 + = error "boink!"; + in' = conv_fn in.value matrix; + } + } + } + } +} + +Filter_rank_item = class + Menupullright "_Rank" "various rank filters" { + Median_item = class + Menuaction "_Median" "3x3 median rank filter" { + action x = map_unary (rank 3 3 4) x; + } + + Image_rank_item = class + Menuaction "_Image Rank" "pixelwise rank a list or group of images" { + action x = class + _result { + _vislevel = 3; + + select + = Expression "Rank" ((int) (guess_size / 2)) + { + guess_size + = len x, is_list x + = len x.value, is_Group x + = 0; + } + + // can't really iterate over groups ... since we allow a group + // argument + _result = rank_image select x; + } + } + + Custom_rank_item = class + Menuaction "Custom _Rank" "rank filter with tuneable parameters" { + action x = class + _result { + _vislevel = 3; + + window_width = Expression "Window width" 3; + window_height = Expression "Window height" 3; + select = Expression "Rank" + ((int) ((to_real window_width * to_real window_height) / 2)); + + _result + = map_unary process x + { + process in + = rank window_width window_height select in; + } + } + } +} + +Filter_morphology_item = class + Menupullright "_Morphology" "various morphological filters" { + /* Some useful masks. + */ + mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; + mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; + mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; + thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; + + Threshold_item = Select_item.Threshold_item; + + sep1 = Menuseparator; + + Dilate_item = class + Menupullright "_Dilate" "morphological dilate" { + Dilate8_item = class + Menuaction "_8-connected" "dilate with an 8-connected mask" { + action x = map_unary (dilate mask8) x; + } + + Dilate4_item = class + Menuaction "_4-connected" "dilate with a 4-connected mask" { + action x = map_unary (dilate mask4) x; + } + } + + Erode_item = class + Menupullright "_Erode" "morphological erode" { + Erode8_item = class + Menuaction "_8-connected" "erode with an 8-connected mask" { + action x = map_unary (erode mask8) x; + } + + Erode4_item = class + Menuaction "_4-connected" "erode with a 4-connected mask" { + action x = map_unary (erode mask4) x; + } + } + + Custom_morph_item = class + Menuaction "Custom _Morphology" + "convolution morphological operator" { + action x = class + _result { + _vislevel = 3; + + mask = mask4; + type = Option "Operation" ["Erode", "Dilate"] 1; + apply = Expression "Number of times to apply mask" 1; + + _result + = map_unary morph x + { + morph image + = Image value' + { + fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); + + value' + = im_erode image.value fatmask, type.value == 0 + = im_dilate image.value fatmask; + } + } + } + } + + sep2 = Menuseparator; + + Open_item = class + Menuaction "_Open" "open with an 8-connected mask" { + action x = map_unary (dilate mask8 @ erode mask8) x; + } + + Close_item = class + Menuaction "_Close" "close with an 8-connected mask" { + action x = map_unary (erode mask8 @ dilate mask8) x; + } + + Clean_item = class + Menuaction "C_lean" "remove 8-connected isolated points" { + action x + = map_unary clean x + { + clean x = x ^ erode mask1 x; + } + } + + Thin_item = class + Menuaction "_Thin" "thin once" { + action x + = map_unary thinall x + { + masks = take 8 (iterate rot45 thin); + thin1 m x = x ^ erode m x; + thinall x = foldr thin1 x masks; + } + } + +} + +Filter_fourier_item = class + Menupullright "_Fourier" "various Fourier filters" { + preview_size = 64; + + sense_option = Option "Sense" [ + "Pass", + "Reject" + ] 0; + + // make a visualisation image + make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) + (im_create_fmask preview_size preview_size); + + // make the process function + process fn in + = (Image @ fn) (im_flt_image_freq in.value); + + New_ideal_item = class + Menupullright "_Ideal" "various ideal Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f sense.value fc.value 0 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 6) fc.value + rw.value 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject ideal Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 12) fcx.value fcy.value + r.value 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } + + New_gaussian_item = class + Menupullright "_Gaussian" "various Gaussian Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 4) fc.value + ac.value 0 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 10) fc.value + rw.value ac.value 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject Gaussian Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + // call a freq func with our parameters + _params f = f (sense.value + 16) fcx.value fcy.value + r.value ac.value 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } + + New_butterworth_item = class + Menupullright "_Butterworth" + "various Butterworth Fourier filters" { + High_low_item = class + Menuaction "_High or Low Pass" + "highpass/lowpass Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 2) o.value fc.value ac.value + 0 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + "ring pass/reject Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 8) o.value fc.value rw.value + ac.value 0; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + "band pass/reject Butterworth Fourier filter" { + action x = class + _result { + _vislevel = 3; + + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + o = Scale "Order" 1 10 2; + + // call a freq func with our parameters + _params f = f (sense.value + 14) o.value fcx.value fcy.value + r.value ac.value; + + visualize_mask = make_vis _params; + + _result = map_unary (process _params) x; + } + } + } +} + +Filter_enhance_item = class + Menupullright "_Enhance" "various enhancement filters" { + Falsecolour_item = class + Menuaction "_False Colour" "false colour a mono image" { + action x = class + _result { + _vislevel = 3; + + o = Scale "Offset" (-255) 255 0; + clip = Toggle "Clip colour range" false; + + _result + = map_unary process x + { + process im + = falsecolour mono'' + { + mono = colour_transform_to Image_type.B_W im; + mono' = mono + o; + mono'' + = (unsigned char) mono', clip + = (unsigned char) (mono' & 0xff); + } + } + } + } + + Statistical_diff_item = class + Menuaction "_Statistical Difference" + "statistical difference of an image" { + action x = class + _result { + _vislevel = 3; + + wsize = Expression "Window size" 11; + tmean = Expression "Target mean" 128; + mean_weight = Scale "Mean weight" 0 1 0.8; + tdev = Expression "Target deviation" 50; + dev_weight = Scale "Deviation weight" 0 1 0.8; + border = Toggle "Output image matches input image in size" true; + + _result + = map_unary process x + { + process in + = Image in'' + { + in' = colour_transform_to Image_type.B_W in.value; + fn + = im_stdif, border + = im_stdif_raw; + in'' = fn in' + mean_weight.value tmean.expr + dev_weight.value tdev.expr + wsize.expr wsize.expr; + } + } + } + } + + Hist_equal_item = class + Menupullright "_Equalise Histogram" "equalise contrast" { + Global_item = class + Menuaction "_Global" "equalise contrast globally" { + action x = map_unary hist_equalize x; + } + + Local_item = class + Menuaction "_Local" "equalise contrast within a roving window" { + action x = class + _result { + _vislevel = 3; + + window_width = Expression "Window width" 20; + window_height = Expression "Window height" 20; + max_slope = Scale "Maxium slope" 0 10 0; + + _result + = map_unary process x + { + process in + = hist_equalize_local + window_width + window_height + max_slope in; + } + } + } + } +} + +Filter_correlate_item = class + Menupullright "Spatial _Correlation" "calculate correlation surfaces" { + Correlate_item = class + Menuaction "_Correlate" "calculate correlation coefficient" { + action a b + = map_binary corr a b + { + corr a b + = correlate a b, + a.width <= b.width && a.height <= b.height + = correlate b a; + } + } + + Correlate_fast_item = class + Menuaction "_Simple Difference" + "calculate sum of squares of differences" { + action a b + = map_binary corr a b + { + corr a b + = correlate_fast a b, + a.width <= b.width && a.height <= b.height + = correlate_fast b a; + } + } +} + +Filter_hough_item = class + Menupullright "_Hough Transform" "transform to parameter space" { + Line_item = class + Menuaction "_Line" "find straight line Hough transform" { + action a = class + _result { + _vislevel = 3; + + pspace_width = Expression "Parameter space width" 64; + pspace_height = Expression "Parameter space height" 64; + + _result + = map_unary line a + { + line a + = hough_line + (to_real pspace_width) (to_real pspace_height) a; + } + } + } + + Circle_item = class + Menuaction "_Circle" "find circle Hough transform" { + action a = class + _result { + _vislevel = 3; + + scale = Expression "Scale down parameter space by" 10; + min_radius = Expression "Minimum radius" 10; + max_radius = Expression "Maximum radius" 30; + + _result + = map_unary circle a + { + circle a + = hough_circle (to_real scale) (to_real min_radius) + (to_real max_radius) a; + } + } + } +} + +Filter_coordinate_item = class + Menupullright "_Coordinate Transform" "various coordinate transforms" { + // run a function which wants a complex arg on a non-complex two-band + // image + run_cmplx fn x + = re x' ++ im x' + { + x' = fn (x?0, x?1); + } + + Polar_item = class + Menuaction "_Polar" "transform to polar coordinates" { + action a = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary to_polar a + { + to_polar im + = mapim interp.value map' im + { + // xy image, origin in the centre, scaled to fit image to + // a circle + xy = make_xy im.width im.height; + xy' = xy - Vector [im.width / 2, im.height / 2]; + scale = min [im.width, im.height] / im.width; + xy'' = 2 * xy' / scale; + + // to polar, scale vertical axis to 360 degrees + map = run_cmplx polar xy''; + map' = map * Vector [1, im.height / 360]; + } + } + } + } + + Rectangular_item = class + Menuaction "_Rectangular" "transform to rectangular coordinates" { + action a = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary to_rect a + { + to_rect im + = mapim interp.value map'' im + { + // xy image, vertical scaled to 360 degrees + xy = make_xy im.width im.height; + xy' = xy * Vector [1, 360 / im.height]; + + // to rect, scale to image rect + map = run_cmplx rectangular xy'; + scale = min [im.width, im.height] / im.width; + map' = map * scale / 2; + + map'' = map' + Vector [im.width / 2, im.height / 2]; + } + } + } + } +} + +#separator + +Filter_tilt_item = class + Menupullright "Ti_lt Brightness" "tilt brightness" { + Left_right_item = class + Menuaction "_Left to Right" "linear left-right brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Left-right tilt" (-1) 1 0; + + _result + = map_unary tilt_lr x + { + tilt_lr image + = image * scale + { + ramp = im_fgrey image.width image.height; + scale = (ramp - 0.5) * tilt + 1; + } + } + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "linear top-bottom brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Top-bottom tilt" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + ramp = rot90 + (im_fgrey image.height image.width); + scale = (ramp - 0.5) * tilt + 1; + } + } + } + } + + sep1 = Menuseparator; + + Left_right_cos_item = class + Menuaction "Cosine Left-_right" "cosine left-right brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Left-right tilt" (-1) 1 0; + shift = Scale "Shift by" (-1) 1 0; + + _result + = map_unary tilt_lr x + { + tilt_lr image + = image * scale + { + ramp = im_fgrey image.width image.height - 0.5 - + shift.value; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } + + Top_bottom_cos_item = class + Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Top-bottom tilt" (-1) 1 0; + shift = Scale "Shift by" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + ramp = rot90 (im_fgrey image.height image.width) - 0.5 - + shift.value; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } + + sep2 = Menuseparator; + + Circular_item = class + Menuaction "_Circular" "circular brighten" { + action x = class + _result { + _vislevel = 3; + + tilt = Scale "Tilt" (-1) 1 0; + hshift = Scale "Horizontal shift by" (-1) 1 0; + vshift = Scale "Vertical shift by" (-1) 1 0; + + _result + = map_unary tilt_tb x + { + tilt_tb image + = image * scale + { + hramp = im_fgrey image.width image.height - 0.5 - + hshift.value; + vramp = rot90 (im_fgrey image.height image.width) - 0.5 - + vshift.value; + ramp = (hramp ** 2 + vramp ** 2) ** 0.5; + scale = 0.5 * tilt.value * cos (ramp * 180) + 1; + } + } + } + } +} + +Filter_blend_item = class + Menupullright "_Blend" "blend objects together" { + Scale_blend_item = class + Menuaction "_Scale" "blend two objects together with a scale" { + action a b = class + _result { + _vislevel = 3; + + p = Scale "Blend position" 0 1 0.5; + + _result + = map_binary process a b + { + process im1 im2 = im1 * (1 - p.value) + im2 * p.value; + } + } + } + + Image_blend_item = class + Menuaction "_Image" "use an image to blend two objects" { + action a b c = class + _result { + _vislevel = 3; + + i = Toggle "Invert mask" false; + + _result + = map_trinary process a b c + { + process a b c + = blend condition in1 in2, !i + = blend (invert condition) in1 in2 + { + compare a b + // prefer image as the condition + = false, + !has_image a && has_image b + // prefer mono images as the condition + = false, + has_bands a && has_bands b && + get_bands a > 1 && get_bands b == 1 + // prefer uchar as the condition + = false, + has_format a && has_format b && + get_format a > Image_format.UCHAR && + get_format b == Image_format.UCHAR + = true; + [condition, in1, in2] = sortc compare [a, b, c]; + } + } + } + } + + Line_blend_item = class + Menuaction "_Along Line" + "blend between image a and image b along a line" { + action a b = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Left to Right", + "Top to Bottom" + ] 0; + blend_position = Scale "Blend position" 0 1 0.5; + blend_width = Scale "Blend width" 0 1 0.05; + + _result + = map_binary process a b + { + process a b + = blend (Image condition) b a + { + output_width = max_pair a.width b.width; + output_height = max_pair a.height b.height; + range + = output_width, orientation == 0 + = output_height; + blend_position' + = floor (range * blend_position.value); + blend_width' + = 1, blend_width.value == 0 + = floor (range * blend_width.value); + start = blend_position' - blend_width' / 2; + + background = (make_xy output_width output_height) >= + blend_position'; + ramp + = im_grey blend_width' output_height, orientation == 0 + = rot90 (im_grey blend_width' output_width); + condition + = insert_noexpand start 0 ramp background?0, + orientation == 0 + = insert_noexpand 0 start ramp background?1; + } + } + } + } + + Blend_alpha_item = class + Menuaction "Blend _Alpha" "blend images with optional alpha channels" { + // usage: layerit foreground background + // input images must be either 1 or 3 bands, optionally + 1 band + // which is used as the alpha channel + // rich lott + + scale_mask im opacity + = (unsigned char) (to_real opacity / 255 * im); + + // to mono + intensity = colour_transform_to Image_type.B_W; + + // All the blend functions + // I am grateful to this page + // http://www.pegtop.net/delphi/blendmodes/ + // for most of the formulae. + + blend_normal mask opacity fg bg + = blend (scale_mask mask opacity) fg bg; + + blend_iflighter mask opacity fg bg + = blend (if fg' > bg' then mask' else 0) fg bg + { + fg' = intensity fg; + bg' = intensity bg; + mask' = scale_mask mask opacity ; + } + + blend_ifdarker mask opacity fg bg + = blend (if fg' < bg' then mask' else 0) fg bg + { + fg' = intensity fg ; + bg' = intensity bg ; + mask' = scale_mask mask opacity ; + } + + blend_multiply mask opacity fg bg + = blend (scale_mask mask opacity) fg' bg + { + fg' = fg / 255 * bg; + } + + blend_add mask opacity fg bg + = blend mask fg' bg + { + fg' = opacity / 255 * fg + bg; + } + + blend_subtract mask opacity fg bg + = blend mask fg' bg + { + fg' = bg - opacity / 255 * fg; + } + + blend_screen mask opacity fg bg + = blend mask fg' bg + { + fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; + } + + blend_burn mask opacity fg bg + = blend mask fg'' bg + { + // fades to white which has no effect. + fg' = (255 - opacity) + opacity * fg / 255; + fg'' = 255 - 255 * (255 - bg) / fg'; + } + + blend_softlight mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; + } + + blend_hardlight mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' + = 2 / 255 * fg * bg, bg < 129 + = 255 - 2 * (255 - bg) * (255 - fg) / 255; + } + + blend_lighten mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = if bg < fg then fg else bg; + } + + blend_darken mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = if bg > fg then fg else bg; + } + + blend_dodge mask opacity fg bg + = blend mask fg'' bg + { + // one added to avoid divide by zero + fg' = 1 + 255 - (opacity / 255 * fg); + fg'' = bg * 255 / fg'; + } + + blend_reflect mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = bg * bg / (255 - fg); + } + + blend_freeze mask opacity fg bg + = blend mask' fg' bg + { + mask' = scale_mask mask opacity; + fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); + } + + blend_or mask opacity fg bg + = bg | (unsigned char) fg' + { + mask' = scale_mask mask opacity; + fg' = fg * mask' / 255; + } + + blend_and mask opacity fg bg + = bg & (unsigned char) fg' + { + mask' = scale_mask mask opacity; + fg' = fg * mask' / 255; + } + + // blend types + NORMAL = 0; + IFLIGHTER = 1; + IFDARKER = 2; + MULTIPLY = 3; + ADD = 4; + SUBTRACT = 5; + SCREEN = 6; + BURN = 7; + DODGE = 8; + HARDLIGHT = 9; + SOFTLIGHT = 10; + LIGHTEN = 11; + DARKEN = 12; + REFLECT = 13; + FREEZE = 14; + OR = 15; + AND = 16; + + // names we show the user for blend types + names = Enum [ + _ "Normal" => NORMAL, + _ "If Lighter" => IFLIGHTER, + _ "If Darker" => IFDARKER, + _ "Multiply" => MULTIPLY, + _ "Add" => ADD, + _ "Subtract" => SUBTRACT, + _ "Screen" => SCREEN, + _ "Burn" => BURN, + _ "Soft Light" => SOFTLIGHT, + _ "Hard Light" => HARDLIGHT, + _ "Lighten" => LIGHTEN, + _ "Darken" => DARKEN, + _ "Dodge" => DODGE, + _ "Reflect" => REFLECT, + _ "Freeze" => FREEZE, + _ "Bitwise OR" => OR, + _ "Bitwise AND" => AND + ]; + + // functions we call for each blend type + actions = Table [ + [NORMAL, blend_normal], + [IFLIGHTER, blend_iflighter], + [IFDARKER, blend_ifdarker], + [MULTIPLY, blend_multiply], + [ADD, blend_add], + [SUBTRACT, blend_subtract], + [SCREEN, blend_screen], + [BURN, blend_burn], + [SOFTLIGHT, blend_softlight], + [HARDLIGHT, blend_hardlight], + [LIGHTEN, blend_lighten], + [DARKEN, blend_darken], + [DODGE, blend_dodge], + [REFLECT, blend_reflect], + [FREEZE, blend_freeze], + [OR, blend_or], + [AND, blend_and] + ]; + + // make sure im has an alpha channel (set opaque if it hasn't) + put_alpha im + = im, im.bands == 4 || im.bands == 2 + = im ++ 255; + + // make sure im has no alpha channel + lose_alpha im + = extract_bands 0 3 im, im.bands == 4 + = im?0, im.bands == 2 + = im; + + // does im have al alpha channel? + has_alpha im = im.bands == 2 || im.bands == 4; + + // get the alpha (set opaque if no alpha) + get_alpha img + = img'?3, img.bands == 4 + = img'?1 + { + img' = put_alpha img; + } + + // add an alpha ... cast the alpha image to match the main image + append_alpha im alpha + = im ++ clip2fmt im.format alpha; + + // makes fg the same size as bg, displaced with u, v pixel offset + moveit fg bg u v + = insert_noexpand u v fg bg' + { + bg' = image_new bg.width bg.height + fg.bands fg.format fg.coding fg.type 0 0 0; + } + + action bg fg = class + _value { + _vislevel = 3; + + method = Option_enum "Blend mode" names "Normal"; + opacity = Scale "Opacity" 0 255 255; + hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; + vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; + + _value + = append_alpha blended merged_alpha, has_alpha bg + = blended + { + // displace and resize fg (need to displace alpha too) + fg' = moveit (put_alpha fg) bg hmove vmove; + + // transform to sRGB + fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); + bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); + + // alphas merged + merged_alpha = get_alpha bg | get_alpha fg'; + + // blend together + blended = (actions.lookup 0 1 method.value_thing) + (get_alpha fg') opacity.value fg'' bg'; + } + } + } +} + +Filter_overlay_header_item = class + Menuaction "_Overlay" + "make a colour overlay of two monochrome images" { + action a b = class + _result { + _vislevel = 3; + + colour = Option "Colour overlay as" [ + _ "Green over Red", + _ "Blue over Red", + _ "Red over Green", + _ "Red over Blue", + _ "Blue over Green", + _ "Green over Blue" + ] 0; + + _result + = map_binary overlay a b + { + overlay a b + = image_set_type Image_type.sRGB + [(a' ++ b' ++ 0), + (a' ++ 0 ++ b'), + (b' ++ a' ++ 0), + (b' ++ 0 ++ a'), + (0 ++ a' ++ b'), + (0 ++ b' ++ a')]?colour + { + a' = colour_transform_to Image_type.B_W a; + b' = colour_transform_to Image_type.B_W b; + } + } + } +} + +Filter_colourize_item = class + Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { + action a b = class + _result { + _vislevel = 3; + + tint = Scale "Tint" 0 1 0.6; + + _result + = map_binary tintit a b + { + tintit a b + = colour_transform_to (get_type colour) colourized' + { + // get the mono thing first + [mono, colour] = + sortc (const (is_colour_type @ get_type)) [a, b]; + + colour' = tint * colour_transform_to Image_type.LAB colour; + mono' = colour_transform_to Image_type.B_W mono; + colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; + colourized' = image_set_type Image_type.LAB colourized; + } + } + } +} + +Filter_browse_multiband_item = class + Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { + Bandwise_item = class + Menuaction "B_andwise" "browse through the bands of a multiband image" { + action image = class + _result { + _vislevel = 3; + + band = Scale "Band" 0 (image.bands - 1) 0; + display = Option "Display as" [ + _ "Grey", + _ "Green over Red", + _ "Blue over Red", + _ "Red over Green", + _ "Red over Blue", + _ "Blue over Green", + _ "Green over Blue" + ] 0; + + _result + = output + { + down = (int) band.value; + up = down + 1; + remainder = band.value - down; + + fade x a + = Vector [0], x == 0 + = a * x; + + a = fade remainder image?up; + b = fade (1 - remainder) image?down; + + output = [ + a + b, + a ++ b ++ 0, + a ++ 0 ++ b, + b ++ a ++ 0, + b ++ 0 ++ a, + 0 ++ a ++ b, + 0 ++ b ++ a + ] ? display; + } + } + } + + Bitwise_item = class + Menuaction "Bi_twise" "browse through the bits of an image" { + action x = class + _result { + _vislevel = 3; + + bit + = Islider "Bit" 0 (nbits - 1) (nbits - 1) + { + nbits + = x.bits, is_Image x + = 8; + Islider c f t v = class + scope.Scale c f t ((int) v) { + Scale = Islider; + } + } + + _result + = map_unary process x + { + process im = (im & (0x1 << bit.value)) != 0; + } + } + } +} + +#separator + +Filter_negative_item = class + Menuaction "Photographic _Negative" "swap black and white" { + action x + = map_unary invert x + { + invert in + = clip2fmt in.format (colour_transform_to (get_type in) rgb') + { + rgb = colour_transform_to Image_type.sRGB in; + rgb' = 255 - rgb; + } + } +} + +Filter_solarize_item = class + Menuaction "_Solarise" "invert colours above a threshold" { + action x = class + _result { + _vislevel = 3; + + kink = Scale "Kink" 0 1 0.5; + + _result + = map_unary process x + { + process image + = hist_map tab'''' image + { + // max pixel value for this format + mx = Image_format.maxval image.format; + + // make a LUT ... just 8 and 16 bit + tab + = im_identity_ushort image.bands mx, + image.format == + Image_format.USHORT + = im_identity image.bands; + tab' = Image tab; + + // make basic ^ shape + tab'' + = tab' * (1 / kink), tab' < mx * kink + = (mx - tab') / (1 - kink); + tab''' = clip2fmt image.format tab''; + + // smooth a bit + mask = matrix_blur (tab'''.width / 8); + tab'''' = convsep mask tab'''; + } + } + } +} + +Filter_diffuse_glow_item = class + Menuaction "_Diffuse Glow" "add a halo to highlights" { + action x = class + _result { + _vislevel = 3; + + r = Scale "Radius" 0 50 5; + highlights = Scale "Highlights" 0 100 95; + glow = Scale "Glow" 0 1 0.5; + colour = Colour_new_item.Widget_colour_item.action; + + _result + = map_unary process x + { + process image + = image' + { + mono = (unsigned char) (colour_transform_to + Image_type.B_W image); + thresh = hist_thresh (highlights.value / 100) mono; + mask = mono > thresh; + blur = convsep (matrix_gaussian_blur r.value) mask; + colour' = colour_transform_to image.type colour; + image' = image + colour' * glow * (blur / 255); + } + } + } +} + +Filter_drop_shadow_item = class + Menuaction "Drop S_hadow" "add a drop shadow to an image" { + action x = class + _result { + _vislevel = 3; + + sx = Scale "Horizontal shadow" (-50) 50 5; + sy = Scale "Vertical shadow" (-50) 50 5; + ss = Scale "Shadow softness" 0 20 5; + bg_colour = Expression "Background colour" 255; + sd_colour = Expression "Shadow colour" 128; + alpha = Toggle "Shadow in alpha channel" false; + transparent = Toggle "Zero pixels are transparent" false; + + _result + = map_unary shadow x + { + shadow image + = Image final + { + blur_size = ss.value * 2 + 1; + + // matrix we blur with to soften shadows + blur_matrix = matrix_gaussian_blur blur_size; + matrix_size = blur_matrix.width; + matrix_radius = (int) (matrix_size / 2) + 1; + + // position and size of shadow image in input cods + // before and after fuzzing + shadow_rect = Rect sx.value sy.value + image.width image.height; + fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; + + // size and pos of final image, in input cods + final_rect = image.rect.union fuzzy_shadow_rect; + + // hard part of shadow in output cods + shadow_rect' = Rect + (shadow_rect.left - final_rect.left) + (shadow_rect.top - final_rect.top) + shadow_rect.width shadow_rect.height; + + // make the shadow mask ... true for parts which cast + // a shadow + mask + = (foldr1 bitwise_and @ bandsplit) (image.value != 0), + transparent + = image_new image.width image.height 1 Image_format.UCHAR + Image_coding.NOCODING Image_type.B_W 255 0 0; + mask' = embed 0 shadow_rect'.left shadow_rect'.top + final_rect.width final_rect.height mask; + mask'' = convsep blur_matrix mask'; + + // use mask to fade between bg and shadow colour + mk_background colour = image_new + final_rect.width final_rect.height + image.bands image.format image.coding image.type + colour 0 0; + + bg_image = mk_background bg_colour.expr; + shadow_image = mk_background sd_colour.expr; + bg = blend mask'' shadow_image bg_image; + + // make a full size mask + fg_mask = embed 0 + (image.rect.left - final_rect.left) + (image.rect.top - final_rect.top) + final_rect.width final_rect.height mask; + + // wrap up the input image ... put the shadow colour + // around it, so if we are outputting a separate + // alpha the shadow colour will be set correctly + fg = insert (image.rect.left - final_rect.left) + (image.rect.top - final_rect.top) + image.value shadow_image; + + final + // make a separate alpha + = fg ++ mask'', alpha + + // paste image over shadow + = if fg_mask then fg else bg; + } + } + } +} + +Filter_paint_text_item = class + Menuaction "_Paint Text" "paint text into an image" { + action x + = paint_position, is_Group x + = paint_area + { + paint_area = class + _result { + _check_args = [ + [x, "x", check_Image] + ]; + _vislevel = 3; + + text = String "Text to paint" "Hello world!"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + align = Option "Alignment" ["Left", "Centre", "Right"] 0; + dpi = Expression "DPI" 300; + colour = Expression "Text colour" 255; + place = Region x (x.width / 4) (x.height / 4) + (x.width / 2) (x.height / 2); + + _result + = insert_noexpand place.left place.top (blend txt' fg place) x + { + fg = image_new place.width place.height x.bands x.format + x.coding x.type colour.expr 0 0; + txt = Image (im_text text.value font.value + place.width align.value (to_real dpi)); + bg = im_black place.width place.height 1; + txt' = insert_noexpand 0 0 txt bg; + } + } + + paint_position = class + _result { + _vislevel = 3; + + text = Pattern_images_item.Text_item.action; + colour = Expression "Text colour" 255; + position = Option "Position" [ + _ "North-west", + _ "North", + _ "North-east", + _ "West", + _ "Centre", + _ "East", + _ "South-west", + _ "South", + _ "South-east", + _ "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_unary paint x + { + paint image + = insert_noexpand x' y' place' image + { + xr = image.width - text.width; + yr = image.height - text.height; + x + = left.expr, position == 9 + = [0, xr / 2, xr]?(position % 3); + y + = top.expr, position == 9 + = [0, yr / 2, yr]?(position / 3); + x' = range 0 x (image.width - 1); + y' = range 0 y (image.height - 1); + w' = range 1 text.width (image.width - x'); + h' = range 1 text.height (image.height - y'); + + place = extract_area x' y' w' h' image; + text' = insert_noexpand 0 0 text (im_black w' h' 1); + fg = image_new w' h' image.bands image.format + image.coding image.type colour.expr 0 0; + place' = blend text' fg place; + } + } + } + } +} + +Autotrace_item = class + Menuaction "_Trace" "convert a bitmap to an SVG file" { + action x = class + _result { + _vislevel = 3; + + despeckle = Scale "Despeckle level" 1 20 1; + line = Scale "Line threshold" 1 20 1; + center = Toggle "Trace centreline" false; + scale = Scale "SVG scale" 0.1 10 1; + + command + = "autotrace %s " ++ join_sep " " + [ofmt, ofile, desp, lint, cent] + { + prog = search_for_error "autotrace"; + ofmt = "-output-format svg"; + ofile = "-output-file %s"; + desp = "-despeckle-level " ++ print despeckle.value; + lint = "-line-threshold " ++ print line.value; + cent = if center then "-centerline " else ""; + } + + _result + = Image output + { + [output] = vips_call "system" + [command] + [$in => [x.value], + $in_format => "%s.ppm", + $out => true, + $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" + ]; + } + } +} + diff --git a/share/nip2/compat/8.6/Histogram.def b/share/nip2/compat/8.6/Histogram.def new file mode 100644 index 00000000..27c61b00 --- /dev/null +++ b/share/nip2/compat/8.6/Histogram.def @@ -0,0 +1,341 @@ +Hist_new_item = class + Menupullright "_New" "new histogram" { + Hist_item = class + Menuaction "_Identity" "make an identity histogram" { + action = class + _result { + _vislevel = 3; + + d = Option "Depth" ["8 bit", "16 bit"] 0; + _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); + } + } + + Hist_new_from_matrix = Matrix_buildlut_item; + + Hist_from_image_item = class + Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { + action x = hist_tag x; + } + + Tone_item = class + Menuaction "_Tone Curve" "make a new tone mapping curve" { + action = class + _result { + _vislevel = 3; + + d = Option "Depth" ["8 bit", "16 bit"] 0; + b = Scale "Black point" 0 100 0; + w = Scale "White point" 0 100 100; + + sp = Scale "Shadow point" 0.1 0.3 0.2; + mp = Scale "Mid-tone point" 0.4 0.6 0.5; + hp = Scale "Highlight point" 0.7 0.9 0.8; + + sa = Scale "Shadow adjust" (-15) 15 0; + ma = Scale "Mid-tone adjust" (-30) 30 0; + ha = Scale "Highlight adjust" (-15) 15 0; + + _result + = tone_build fmt b w sp mp hp sa ma ha + { + fmt = [Image_format.UCHAR, Image_format.USHORT]?d; + } + } + } +} + +Hist_convert_to_hist_item = class + Menuaction "Con_vert to Histogram" "convert anything to a histogram" { + action x = hist_tag (to_image x); +} + +Hist_find_item = class + Menupullright "_Find" "find a histogram" { + Oned_item = class + Menuaction "_One Dimension" + "for a n-band image, make an n-band 1D histogram" { + action x = map_unary hist_find x; + } + + Nd_item = class + Menuaction "_Many Dimensions" + "for a n-band image, make an n-dimensional histogram" { + action x = class + _result { + _vislevel = 3; + + // default to something small-ish + bins = Expression "Number of bins in each dimension" 8; + + _result + = map_unary process x + { + process in + = hist_find_nD bins in; + } + } + } + + Indexed_item = class + Menuaction "_Indexed" + "use a 1-band index image to pick bins for an n-band image" { + action x y = class + _result { + _vislevel = 3; + + combine = Combine_picker Combine_type.SUM; + + _result + = map_binary map x y + { + map a b + = hist_find_indexed combine.value index im + { + [im, index] = sortc (const is_index) [a, b]; + + is_index x + = has_image x && b == 1 && + (f == Image_format.UCHAR || f == Image_format.USHORT) + { + im = get_image x; + b = get_bands x; + f = get_format x; + } + } + } + } + } +} + +Hist_map_item = class + Menuaction "_Map" "map an image through a histogram" { + action x y + = map_binary map x y + { + map a b + = hist_map hist im + { + [im, hist] = sortc (const is_hist) [a, b]; + } + } +} + +Hist_eq_item = Filter_enhance_item.Hist_equal_item; + +#separator + +Hist_cum_item = class + Menuaction "_Integrate" + "form cumulative histogram" { + action x = map_unary hist_cum x; +} + +Hist_diff_item = class + Menuaction "_Differentiate" + "find point-to-point differences (inverse of Integrate)" { + action x = map_unary hist_diff x; +} + +Hist_norm_item = class + Menuaction "N_ormalise" "normalise a histogram" { + action x = map_unary hist_norm x; +} + +Hist_inv_item = class + Menuaction "In_vert" "invert a histogram" { + action x = map_unary hist_inv x; +} + +Hist_match_item = class + Menuaction "Ma_tch" + "find LUT which will match first histogram to second" { + action in ref = map_binary hist_match in ref; +} + +Hist_zerox_item = class + Menuaction "_Zero Crossings" "find zero crossings" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Direction" [ + "Positive-going", + "Negative-going" + ] 0; + + _result + = map_unary (zerox (if edge == 0 then -1 else 1)) x; + } +} + +Hist_entropy_item = class Menuaction "Entropy" "calculate histogram entropy" { + action x = hist_entropy x; +} + +#separator + +Hist_profile_item = class + Menuaction "Find _Profile" + "search from image edges for non-zero pixels" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Search from" [ + "Top edge down", + "Left edge to right", + "Bottom edge up", + "Right edge to left" + ] 2; + + _result + = map_unary profile x + { + profile image + = (Plot_histogram @ hist_tag) [ + profilemb 0 image.value, + profilemb 1 image.value, + profilemb 0 (fliptb image.value), + profilemb 1 (fliplr image.value) + ]?edge; + + // im_profile only does 1 band images :-( + profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; + } + } +} + +Hist_project_item = class + Menuaction "Find Pro_jections" + "find horizontal and vertical projections" { + action x = class { + _vislevel = 2; + + _result = map_unary project x; + + // extract the result ... could be a group + extr n + = Plot_histogram _result?n, is_list _result + = Group (map (Plot_histogram @ converse subscript n) _result.value); + + horizontal = extr 0; + vertical = extr 1; + centre = (gravity horizontal, gravity vertical); + } +} + +#separator + +Hist_graph_item = class + Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { + action x = class + _value { + _vislevel = 3; + + width = Scale "Width" 1 40 1; + displace = Scale "Horizontal displace" (-50) 50 0; + vdisplace = Scale "Vertical displace" (-50) 50 0; + + _value + = map_unary graph x + { + graph arrow + = hist_tag area' + { + area = extract_arrow + displace.value vdisplace.value width.value arrow; + + // squish vertically to get an average + area' = resize Kernel_linear 1 (1 / width.value) area; + } + } + } +} + +Extract_arrow_item = class + Menuaction "Extract _Arrow" "extract the area around an arrow" { + action x = class + _value { + _vislevel = 3; + + width = Scale "Width" 1 40 1; + displace = Scale "Horizontal displace" (-50) 50 0; + vdisplace = Scale "Vertical displace" (-50) 50 0; + + _value + = map_unary (extract_arrow + displace.value vdisplace.value width.value) x; + } +} + +Hist_plot_item = class + Menuaction "Plot _Object" + "plot an object as a bar, point or line graph" { + action x = class + _result { + _vislevel = 3; + + caption = Expression "Chart caption" "none"; + format = Option_enum "Format" Plot_format.names "YYYY"; + style = Option_enum "Style" Plot_style.names "Line"; + + auto = Toggle "Auto Range" true; + xmin = Expression "X range minimum" 0; + xmax = Expression "X range maximum" 1; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + xcaption = Expression "X axis caption" "none"; + ycaption = Expression "Y axis caption" "none"; + series_captions = Expression "Series captions" ["Band 0"]; + + _result + = Plot options (image x) + { + options + = [$style => style.value, $format => format.value] ++ + range ++ captions; + range + = [], auto + = [$xmin => xmin.expr, $xmax => xmax.expr, + $ymin => ymin.expr, $ymax => ymax.expr]; + + captions + = concat (map test caption_options) ++ + [$series_captions => series_captions.expr] + { + caption_options = [ + $caption => caption.expr, + $xcaption => xcaption.expr, + $ycaption => ycaption.expr + ]; + test x + = [], value == "none" + = [option_name => value] + { + [option_name, value] = x; + } + } + + image x + = image (extract_arrow 0 0 1 x), is_Arrow x + = get_image x, has_image x + = x2b im, b == 1 + = im + { + im = get_image (to_image x); + w = get_width im; + h = get_height im; + b = get_bands im; + + // matrix to image makes a 1-band mxn image + // we need to put columns into bands + x2b im + = bandjoin (map extract_col [0 .. w - 1]) + { + extract_col x = extract_area x 0 1 h im; + } + } + } + } +} diff --git a/share/nip2/compat/8.6/Image.def b/share/nip2/compat/8.6/Image.def new file mode 100644 index 00000000..a010d989 --- /dev/null +++ b/share/nip2/compat/8.6/Image.def @@ -0,0 +1,2329 @@ +Image_new_item = class Menupullright "_New" "make new things" { + Image_black_item = class Menuaction "_Image" "make a new image" { + format_names = [ + "8-bit unsigned int - UCHAR", // 0 + "8-bit signed int - CHAR", // 1 + "16-bit unsigned int - USHORT", // 2 + "16-bit signed int - SHORT", // 3 + "32-bit unsigned int - UINT", // 4 + "32-bit signed int - INT", // 5 + "32-bit float - FLOAT", // 6 + "64-bit complex - COMPLEX", // 7 + "64-bit float - DOUBLE", // 8 + "128-bit complex - DPCOMPLEX" // 9 + ]; + + action = class + Image _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + nbands = Expression "Image bands" 1; + format_option = Option "Image format" format_names 0; + type_option = Option_enum "Image type" + Image_type.type_names "B_W"; + pixel = Expression "Pixel value" 0; + + _result + = image_new (to_real nwidth) (to_real nheight) (to_real nbands) + (to_real format_option) Image_coding.NOCODING + type_option.value_thing pixel.expr 0 0; + } + } + + Image_new_from_image_item = class + Menuaction "_From Image" "make a new image based on image x" { + action x = class + Image _result { + _vislevel = 3; + + pixel = Expression "Pixel value" 0; + + _result + = image_new x.width x.height x.bands + x.format x.coding x.type pixel.expr x.xoffset x.yoffset; + } + } + + Image_region_item = class + Menupullright "_Region on Image" "make a new region on an image" { + Region_item = class + Menuaction "_Region" "make a region on an image" { + action image = scope.Region_relative image 0.25 0.25 0.5 0.5; + } + + Mark_item = class + Menuaction "_Point" "make a point on an image" { + action image = scope.Mark_relative image 0.5 0.5; + } + + Arrow_item = class + Menuaction "_Arrow" "make an arrow on an image" { + action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; + } + + HGuide_item = class + Menuaction "_Horizontal Guide" + "make a horizontal guide on an image" { + action image = scope.HGuide image 0.5; + } + + VGuide_item = class + Menuaction "_Vertical Guide" "make a vertical guide on an image" { + action image = scope.VGuide image 0.5; + } + + sep1 = Menuseparator; + + Move_item = class + Menuaction "From Region" + "new region on image using existing region as a guide" { + action a b + = map_binary process a b + { + process a b + = x.Region target x.left x.top x.width x.height, + is_Region x + = x.Arrow target x.left x.top x.width x.height, + is_Arrow x + = error "bad arguments to region-from-region" + { + // prefer image then region + compare a b + = false, + !is_Image a && is_Image b + = false, + is_Region a && !is_Region b + = true; + + [target, x] = sortc compare [a, b]; + } + } + } + } +} + +Image_convert_to_image_item = class + Menuaction "Con_vert to Image" "convert anything to an image" { + action x = to_image x; +} + +Image_number_format_item = class + Menupullright "_Format" "convert numeric format" { + + U8_item = class + Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { + action x = map_unary cast_unsigned_char x; + } + + U16_item = class + Menuaction "1_6 bit unsigned" + "convert to unsigned 16 bit [0, 65535]" { + action x = map_unary cast_unsigned_short x; + } + + U32_item = class + Menuaction "_32 bit unsigned" + "convert to unsigned 32 bit [0, 4294967295]" { + action x = map_unary cast_unsigned_int x; + } + + sep1 = Menuseparator; + + S8_item = class + Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { + action x = map_unary cast_signed_char x; + } + + S16_item = class + Menuaction "16 b_it signed" + "convert to signed 16 bit [-32768, 32767]" { + action x = map_unary cast_signed_short x; + } + + S32_item = class + Menuaction "32 bi_t signed" + "convert to signed 32 bit [-2147483648, 2147483647]" { + action x = map_unary cast_signed_int x; + } + + sep2 = Menuseparator; + + Float_item = class + Menuaction "_Single precision float" + "convert to IEEE 32 bit float" { + action x = map_unary cast_float x; + } + + Double_item = class + Menuaction "_Double precision float" + "convert to IEEE 64 bit float" { + action x = map_unary cast_double x; + } + + sep3 = Menuseparator; + + Scmplxitem = class + Menuaction "Single _precision complex" + "convert to 2 x IEEE 32 bit float" { + action x = map_unary cast_complex x; + } + + Dcmplx_item = class + Menuaction "Double p_recision complex" + "convert to 2 x IEEE 64 bit float" { + action x = map_unary cast_double_complex x; + } +} + +Image_header_item = class + Menupullright "_Header" "do stuff to the image header" { + + Image_get_item = class + Menupullright "_Get" "get header fields" { + + // the header fields we can get + fields = class { + type = 0; + width = 1; + height = 2; + format = 3; + bands = 4; + xres = 5; + yres = 6; + xoffset = 7; + yoffset = 8; + coding = 9; + + field_names = Enum [ + $width => width, + $height => height, + $bands => bands, + $format => format, + $type => type, + $xres => xres, + $yres => yres, + $xoffset => xoffset, + $yoffset => yoffset, + $coding => coding + ]; + + field_option name = Option_enum (_ "Field") field_names name; + + field_funcs = Table [ + [type, get_type], + [width, get_width], + [height, get_height], + [format, get_format], + [bands, get_bands], + [xres, get_xres], + [yres, get_yres], + [xoffset, get_xoffset], + [yoffset, get_yoffset], + [coding, get_coding] + ]; + } + + get_field field_name x = class + _result { + _vislevel = 3; + + field = fields.field_option field_name; + + _result + = map_unary (Real @ + fields.field_funcs.lookup 0 1 field.value_thing) x; + } + + Width_item = class + Menuaction "_Width" "get width" { + action x = get_field "width" x; + } + + Height_item = class + Menuaction "_Height" "get height" { + action x = get_field "height" x; + } + + Bands_item = class + Menuaction "_Bands" "get bands" { + action x = get_field "bands" x; + } + + Format_item = class + Menuaction "_Format" "get format" { + action x = get_field "format" x; + } + + Type_item = class + Menuaction "_Type" "get type" { + action x = get_field "type" x; + } + + Xres_item = class + Menuaction "_Xres" "get X resolution" { + action x = get_field "xres" x; + } + + Yres_item = class + Menuaction "_Yres" "get Y resolution" { + action x = get_field "yres" x; + } + + Xoffset_item = class + Menuaction "X_offset" "get X offset" { + action x = get_field "xoffset" x; + } + + Yoffset_item = class + Menuaction "Yo_ffset" "get Y offset" { + action x = get_field "yoffset" x; + } + + Coding_item = class + Menuaction "_Coding" "get coding" { + action x = get_field "coding" x; + } + + sep1 = Menuseparator; + + Custom_item = class + Menuaction "C_ustom" "get any header field" { + action x = class + _result { + _vislevel = 3; + + field = String "Field" "Xsize"; + parse = Option "Parse" [ + "No parsing", + "Parse string as integer", + "Parse string as real", + "Parse string as hh:mm:ss" + ] 0; + + _result + = map_unary (wrap @ process @ get_header field.value) x + { + parse_str parse str = parse (split is_space str)?0; + + parse_field name cast parse x + = cast x, is_number x + = parse_str parse x, is_string x + = error ("not " ++ name); + + get_int = parse_field "int" + cast_signed_int parse_int; + get_float = parse_field "float" + cast_float parse_float; + get_time = parse_field "hh:mm:ss" + cast_signed_int parse_time; + + wrap x + = Real x, is_real x + = Vector x, is_real_list x + = Image x, is_image x + = Bool x, is_bool x + = Matrix x, is_matrix x + = String "String" x, is_string x + = List x, is_list x + = x; + + process = [ + id, + get_int, + get_float, + get_time + ]?parse; + } + } + } + } + + sep1 = Menuseparator; + + Image_set_meta_item = class + Menuaction "_Set" "set image metadata" { + action x = class + _result { + _vislevel = 3; + + fname = String "Field" "field-name"; + val = Expression "Value" 42; + + _result + = map_unary process x + { + process image + = set_header fname.value val.expr image; + } + } + } + + Image_edit_header_item = class + Menuaction "_Edit" "change advisory header fields of image" { + type_names = Image_type.type_names; + all_names = sort (map (extract 0) type_names.value); + + get_prop has get def x + = get x, has x + = def; + + action x = class + _result { + _vislevel = 3; + + nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); + nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); + nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); + nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); + + type_option + = Option_enum "Image type" Image_type.type_names + (Image_type.type_names.get_name type) + { + type + = x.type, is_Image x + = Image_type.MULTIBAND; + } + + _result + = map_unary process x + { + process image + = Image (im_copy_set image.value type_option.value_thing + (to_real nxres) (to_real nyres) + (to_real nxoff) (to_real nyoff)); + } + } + } +} + +Image_cache_item = class + Menuaction "C_ache" "cache calculated image pixels" { + action x = class + _result { + _vislevel = 3; + + tile_width = Number "Tile width" 128; + tile_height = Number "Tile height" 128; + max_tiles = Number "Maximum number of tiles to cache" (-1); + + _result + = map_unary process x + { + process image + = cache (to_real tile_width) (to_real tile_height) + (to_real max_tiles) image; + } + } +} + +#separator + +Image_levels_item = class + Menupullright "_Levels" "change image levels" { + Scale_item = class + Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { + action x = map_unary scale x; + } + + Linear_item = class + Menuaction "_Linear" "linear transform of image levels" { + action x = class + _result { + _vislevel = 3; + + scale = Scale "Scale" 0.001 3 1; + offset = Scale "Offset" (-128) 128 0; + + _result + = map_unary adj x + { + adj x + // only force back to input type if this is a thing + // with a type ... so we work for Colour / Matrix etc. + = clip2fmt x.format x', has_member "format" x + = x' + { + x' = x * scale + offset; + } + } + } + } + + Gamma_item = class + Menuaction "_Power" "power transform of image levels (gamma)" { + action x = class + _result { + _vislevel = 3; + + gamma = Scale "Gamma" 0.001 4 1; + image_maximum_hint = "You may need to change image_maximum if " ++ + "this is not an 8 bit image"; + im_mx + = Expression "Image maximum" mx + { + mx + = Image_format.maxval x.format, has_format x + = 255; + } + + _result + = map_unary gam x + { + gam x + = clip2fmt (get_format x) x', has_format x + = x' + { + x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; + } + } + } + } + + Tone_item = class + Menuaction "_Tone Curve" "adjust tone curve" { + action x = class + _result { + _vislevel = 3; + + b = Scale "Black point" 0 100 0; + w = Scale "White point" 0 100 100; + + sp = Scale "Shadow point" 0.1 0.3 0.2; + mp = Scale "Mid-tone point" 0.4 0.6 0.5; + hp = Scale "Highlight point" 0.7 0.9 0.8; + + sa = Scale "Shadow adjust" (-15) 15 0; + ma = Scale "Mid-tone adjust" (-30) 30 0; + ha = Scale "Highlight adjust" (-15) 15 0; + + curve = tone_build x.format b w sp mp hp sa ma ha; + + _result = map_unary (hist_map curve) x; + } + } +} + +Image_transform_item = class + Menupullright "_Transform" "transform images" { + Rotate_item = class + Menupullright "Ro_tate" "rotate image" { + Fixed_item = class + Menupullright "_Fixed" "clockwise rotation by fixed angles" { + rotate_widget default x = class + _result { + _vislevel = 3; + + angle = Option "Rotate by" [ + "Don't rotate", + "90 degrees clockwise", + "180 degrees", + "90 degrees anticlockwise" + ] default; + + _result + = map_unary process x + { + process = [ + // we can't use id here since we want to "declass" + // the members of x ... consider if x is a crop class, + // for example, we don't want to inherit from crop, we + // want to make a new image class + rot180 @ rot180, + rot90, + rot180, + rot270 + ] ? angle; + } + } + + Rot90_item = class + Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { + action x = rotate_widget 1 x; + } + + Rot180_item = class + Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { + action x = rotate_widget 2 x; + } + + Rot270_item = class + Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { + action x = rotate_widget 3 x; + } + } + + Free_item = class + Menuaction "_Free" "clockwise rotation by any angle" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "Angle" (-180) 180 0; + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary process x + { + process image + = rotate interp angle image; + } + } + } + + Straighten_item = class + Menuaction "_Straighten" + ("smallest rotation that makes an arrow either horizontal " ++ + "or vertical") { + action x = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_unary straighten x + { + straighten arrow + = rotate interp angle'' arrow.image + { + x = arrow.width; + y = arrow.height; + + angle = im (polar (x, y)); + + angle' + = angle - 360, angle > 315 + = angle - 180, angle > 135 + = angle; + + angle'' + = -angle', angle' >= (-45) && angle' < 45 + = 90 - angle'; + } + } + } + } + } + + Flip_item = class + Menupullright "_Flip" "mirror left/right or up/down" { + Left_right_item = class + Menuaction "_Left Right" "mirror object left/right" { + action x = map_unary fliplr x; + } + + Top_bottom_item = class + Menuaction "_Top Bottom" "mirror object top/bottom" { + action x = map_unary fliptb x; + } + } + + Resize_item = class + Menupullright "_Resize" "change image size" { + Scale_item = class + Menuaction "_Scale" "scale image size by a factor" { + action x = class + _result { + _vislevel = 3; + + xfactor = Expression "Horizontal scale factor" 1; + yfactor = Expression "Vertical scale factor" 1; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + process image + = resize kernel xfactor yfactor image; + } + } + } + + Size_item = class + Menuaction "_Size To" "resize to a fixed size" { + action x = class + _result { + _vislevel = 3; + + which = Option "Resize axis" [ + "Shortest", + "Longest", + "Horizontal", + "Vertical" + ] 0; + size = Expression "Resize to (pixels)" 128; + aspect = Toggle "Break aspect ratio" false; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + process image + = resize kernel h v image, aspect + = resize kernel fac fac image + { + xfac = to_real size / image.width; + yfac = to_real size / image.height; + max_factor + = [xfac, 1], xfac > yfac + = [1, yfac]; + min_factor + = [xfac, 1], xfac < yfac + = [1, yfac]; + [h, v] = [ + max_factor, + min_factor, + [xfac, 1], + [1, yfac]]?which; + + fac + = h, v == 1 + = v; + } + } + } + } + + Size_within_item = class + Menuaction "Size _Within" "size to fit within a rectangle" { + action x = class + _result { + _vislevel = 3; + + // the rects we size to fit within + _rects = [ + [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], + [1280, 1024], [1024, 768], [800, 600], [640, 480] + ]; + + within = Option "Fit within (pixels)" ( + [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ + ["Custom"] + ) 4; + custom_width = Expression "Custom width" 1000; + custom_height = Expression "Custom height" 1000; + size = Option "Page size" [ + "Full page", "Half page", "Quarter page" + ] 0; + kernel = Kernel_picker Kernel_type.LINEAR; + + _result + = map_unary process x + { + xdiv = [1, 2, 2]?size; + ydiv = [1, 1, 2]?size; + allrect = _rects ++ [ + [custom_width.expr, custom_height.expr] + ]; + [width, height] = allrect?within; + + process x + = resize kernel fac fac x, fac < 1 + = x + { + xfac = (width / xdiv) / x.width; + yfac = (height / ydiv) / x.height; + fac = min_pair xfac yfac; + } + } + } + } + + Resize_canvas_item = class + Menuaction "_Canvas" "change size of surrounding image" { + action x = class + _result { + _vislevel = 3; + + // try to guess a sensible size for the new image + _guess_size + = x.rect, is_Image x + = Rect 0 0 100 100; + + nwidth = Expression "New width (pixels)" _guess_size.width; + nheight = Expression "New height (pixels)" _guess_size.height; + bgcolour = Expression "Background colour" 0; + + position = Option "Position" [ + "North-west", + "North", + "North-east", + "West", + "Centre", + "East", + "South-west", + "South", + "South-east", + "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_unary process x + { + process image + = insert_noexpand xp yp image background + { + width = image.width; + height = image.height; + coding = image.coding; + bands + = 3, coding == Image_coding.LABPACK + = image.bands; + format + = Image_format.FLOAT, coding == Image_coding.LABPACK + = image.format; + type = image.type; + + // placement vectors ... left, centre, right + xposv = [0, to_real nwidth / 2 - width / 2, + to_real nwidth - width]; + yposv = [0, to_real nheight / 2 - height / 2, + to_real nheight - height]; + xp + = left, position == 9 + = xposv?((int) (position % 3)); + yp + = top, position == 9 + = yposv?((int) (position / 3)); + + background = image_new nwidth nheight + bands format coding type bgcolour.expr 0 0; + } + } + } + } + } + + Image_map_item = class + Menuaction "_Map" "map an image through a 2D transform image" { + action a b = class + _result { + _vislevel = 3; + + interp = Interpolate_picker Interpolate_type.BILINEAR; + + _result + = map_binary trans a b + { + trans a b + = mapim interp.value in index + { + // get the index image first + [index, in] = sortc (const is_twocomponent) [a, b]; + + // is a two-component image, ie. one band complex, or + // two-band non-complex + is_twocomponent x + = is_nonc x || is_c x; + is_nonc x + = has_bands x && get_bands x == 2 && + has_format x && !is_complex_format (get_format x); + is_c x + = has_bands x && get_bands x == 1 && + has_format x && is_complex_format (get_format x); + is_complex_format f + = f == Image_format.COMPLEX || + f == Image_format.DPCOMPLEX; + } + } + } + } + + Image_perspective_item = Perspective_item; + + Image_rubber_item = class + Menupullright "Ru_bber Sheet" + "automatically warp images to superposition" { + rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; + rubber_order = Option "Order" ["0", "1", "2", "3"] 1; + rubber_wrap = Toggle "Wrap image edges" false; + + // a transform ... a matrix, plus the size of the image the + // matrix was made for + Transform matrix image_width image_height = class + matrix { + // scale a transform ... if it worked for a m by n image, make + // it work for a (m * xfac) by (y * yfac) image + rescale xfac yfac + = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) + (image_width * xfac) (image_height * yfac) + { + facs = [ + [xfac, yfac], + [1, 1], + [1, 1], + [1 / xfac, 1 / yfac], + [1 / xfac, 1 / yfac], + [1 / xfac, 1 / yfac] + ]; + } + } + + // yuk!!!! fix is_instanceof to not need absolute names + is_Transform = is_instanceof + "Image_transform_item.Image_rubber_item.Transform"; + + Find_item = class + Menuaction "_Find" + ("find a transform which will map sample image onto " ++ + "reference") { + action reference sample = class + _trn { + _vislevel = 3; + + // controls + order = rubber_order; + interp = rubber_interp; + wrap = rubber_wrap; + max_err = Expression "Maximum error" 0.3; + max_iter = Expression "Maximum iterations" 10; + + // transform + [sample', trn, err] = transform_search + max_err max_iter order interp wrap + sample reference; + transformed_image = Image sample'; + _trn = Transform trn reference.width reference.height; + final_error = err; + } + } + + Apply_item = class + Menuaction "_Apply" "apply a transform to an image" { + action a b = class + _result { + _vislevel = 3; + + // controls + interp = rubber_interp; + wrap = rubber_wrap; + + _result + = map_binary trans a b + { + trans a b + = transform interp wrap t' i + { + // get the transform arg first + [i, t] = sortc (const is_Transform) [a, b]; + t' = t.rescale (i.width / t.image_width) + (i.height / t.image_height); + } + } + } + } + } + + sep1 = Menuseparator; + + Match_item = class + Menuaction "_Linear Match" + "rotate and scale one image to match another" { + action x y = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + _b = find_image y; + + ap1 = Mark_relative _a 0.5 0.25; + bp1 = Mark_relative _b 0.5 0.25; + ap2 = Mark_relative _a 0.5 0.75; + bp2 = Mark_relative _b 0.5 0.75; + + refine = Toggle "Refine selected tie-points" false; + lock = Toggle "No resize" false; + + _result + = map_binary process x y + { + process a b + = Image b''' + { + _prefs = Workspaces.Preferences; + window = _prefs.MOSAIC_WINDOW_SIZE; + object = _prefs.MOSAIC_OBJECT_SIZE; + + a' = a.value; + b' = b.value; + + b'' = clip2fmt a.format b'; + + // return p2 ... if lock is set, return a p2 a standard + // distance along the vector joining p1 and p2 + norm p1 p2 + = Rect left' top' 0 0, lock + = p2 + { + v = (p2.left - p1.left, p2.top - p1.top); + // 100000 to give precision since we pass points as + // ints to match + n = 100000 * sign v; + left' = p1.left + re n; + top' = p1.top + im n; + } + + ap2'' = norm ap1 ap2; + bp2'' = norm bp1 bp2; + + b''' + = im_match_linear_search a' b'' + ap1.left ap1.top bp1.left bp1.top + ap2''.left ap2''.top bp2''.left bp2''.top + object window, + // we can't search if lock is on + refine && !lock + = im_match_linear a' b'' + ap1.left ap1.top bp1.left bp1.top + ap2''.left ap2''.top bp2''.left bp2''.top; + } + } + } + } + + Image_perspective_match_item = Perspective_match_item; +} + +Image_band_item = class + Menupullright "_Band" "manipulate image bands" { + // like extract_bands, but return [] for zero band image + // makes compose a bit simpler + exb b n x + = [], to_real n == 0 + = extract_bands b n x; + + Extract_item = class Menuaction "_Extract" "extract bands from image" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from band" 0; + number = Expression "Extract this many bands" 1; + + _result = map_unary (exb first number) x; + } + } + + Insert_item = class Menuaction "_Insert" "insert bands into image" { + action x y = class + _result { + _vislevel = 3; + + first = Expression "Insert at position" 0; + + _result + = map_binary process x y + { + process im1 im2 + = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 + { + f = to_real first; + b = im1.bands; + } + } + } + } + + Delete_item = class Menuaction "_Delete" "delete bands from image" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from band" 0; + number = Expression "Delete this many bands" 1; + + _result + = map_unary process x + { + process im + = exb 0 f im ++ exb (f + n) (b - (f + n)) im + { + f = to_real first; + n = to_real number; + b = im.bands; + } + } + } + } + + Bandwise_item = Image_join_item.Bandwise_item; + + sep1a = Menuseparator; + + Bandand_item = class + Menuaction "Bitwise Band AND" "bitwise AND of image bands" { + action x = bandand x; + } + + Bandor_item = class + Menuaction "Bitwise Band OR" "bitwise OR of image bands" { + action x = bandor x; + } + + sep2 = Menuseparator; + + To_dimension_item = class + Menuaction "To D_imension" "convert bands to width or height" { + action x = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + + _result + = map_unary process x + { + process im + = foldl1 [join_lr, join_tb]?orientation (bandsplit im); + } + } + } + + To_bands_item = class + Menuaction "To B_ands" "turn width or height to bands" { + action x = class + _result { + _vislevel = 3; + + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + + _result + = map_unary process x + { + process im + = bandjoin (map extract_column [0 .. im.width - 1]), + orientation == 0 + = bandjoin (map extract_row [0 .. im.height - 1]) + { + extract_column n + = extract_area n 0 1 im.height im; + extract_row n + = extract_area 0 n im.width 1 im; + } + } + } + } +} + +Image_alpha_item = class + Menupullright "_Alpha" "manipulate image alpha" { + + Add_item = class Menuaction "_Add" "add alpha" { + action x = class + _result { + _vislevel = 3; + + opacity = Expression "Opacity (255 == solid)" 255; + + _result = x ++ to_real opacity; + } + } + + Flatten_item = class Menuaction "_Flatten" "flatten alpha out of image" { + action x = class + _result { + _vislevel = 3; + + bg = Expression "Background" 0; + + _result = map_unary (flattenimage bg) x; + } + } + + Extract_item = class Menuaction "_Extract" "extract alpha" { + action x + = map_unary exb x + { + exb x = extract_bands (x.bands - 1) 1 x; + } + } + + Drop_item = class Menuaction "_Drop" "drop alpha" { + action x + = map_unary exb x + { + exb x = extract_bands 0 (x.bands - 1) x; + } + } + + sep1 = Menuseparator; + + Premultiply_item = class Menuaction "_Premultiply" "premultiply alpha" { + action x = premultiply x; + } + + Unpremultiply_item = class + Menuaction "_Unpremultiply" "unpremultiply alpha" { + action x = unpremultiply x; + } + + sep2 = Menuseparator; + + Composite2_item = class + Menuaction "_Composite two" "composite a pair of images" { + action x y = class + _result { + _vislevel = 3; + + blend = Option_enum (_ "Blend mode") modes "over" + { + modes = Blend_type.types; + } + compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" + { + spaces = Image_type.image_colour_spaces; + } + premultiplied = Toggle (_ "Premultiplied") false; + + _result + = Image output + { + [output] = vips_call "composite" + [[y.value, x.value], blend.value] + [$compositing_space => compositing_space.value_thing, + $premultiplied => premultiplied.value + ]; + } + } + } + + Composite3_item = class + Menuaction "_Composite three" "composite three images" { + action x y z = class + _result { + _vislevel = 3; + + blend1 = Option_enum (_ "Blend mode") modes "over" + { + modes = Blend_type.types; + } + blend2 = Option_enum (_ "Blend mode") modes "over" + { + modes = Blend_type.types; + } + compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" + { + spaces = Image_type.image_colour_spaces; + } + premultiplied = Toggle (_ "Premultiplied") false; + + _result + = Image output + { + [output] = vips_call "composite" + [[z.value, y.value, x.value], [blend1.value, blend2.value]] + [$compositing_space => compositing_space.value_thing, + $premultiplied => premultiplied.value + ]; + } + } + } +} + +Image_crop_item = class + Menuaction "_Crop" "extract a rectangular area from an image" { + action x + = crop x [l, t, w, h] + { + fields = [ + [has_left, get_left, 0], + [has_top, get_top, 0], + [has_width, get_width, 100], + [has_height, get_height, 100] + ]; + + [l, t, w, h] + = map get_default fields + { + get_default line + = get x, has x + = default + { + [has, get, default] = line; + } + } + } + + crop x geo = class + _result { + _vislevel = 3; + + l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); + t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); + w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); + h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); + + _result + = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] + { + extract im l t w h + = extract_area left' top' width' height' im + { + width' = min_pair (to_real w) im.width; + height' = min_pair (to_real h) im.height; + left' = range 0 (to_real l) (im.width - width'); + top' = range 0 (to_real t) (im.height - height'); + } + } + } +} + +Image_insert_item = class + Menuaction "_Insert" "insert a small image into a large image" { + action a b + = insert_position, is_Group a || is_Group b + = insert_area + { + insert_area = class + _result { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _vislevel = 3; + + // sort to get smallest first + _pred x y = x.width * x.height < y.width * y.height; + [_a', _b'] = sortc _pred [a, b]; + + place + = Area _b' left top width height + { + // be careful in case b is smaller than a + left = max_pair 0 ((_b'.width - _a'.width) / 2); + top = max_pair 0 ((_b'.height - _a'.height) / 2); + width = min_pair _a'.width _b'.width; + height = min_pair _a'.height _b'.height; + } + + _result + = insert_noexpand place.left place.top + (clip2fmt _b'.format a'') _b' + { + a'' = extract_area 0 0 place.width place.height _a'; + } + } + + insert_position = class + _result { + _vislevel = 3; + + position = Option "Position" [ + "North-west", + "North", + "North-east", + "West", + "Centre", + "East", + "South-west", + "South", + "South-east", + "Specify in pixels" + ] 4; + left = Expression "Pixels from left" 0; + top = Expression "Pixels from top" 0; + + _result + = map_binary insert a b + { + insert a b + = insert_noexpand left top (clip2fmt b.format a) b, + position == 9 + = insert_noexpand xp yp (clip2fmt b.format a) b + { + xr = b.width - a.width; + yr = b.height - a.height; + xp = [0, xr / 2, xr]?((int) (position % 3)); + yp = [0, yr / 2, yr]?((int) (position / 3)); + } + } + } + } +} + +Image_select_item = Select_item; + +Image_draw_item = class + Menupullright "_Draw" "draw lines, circles, rectangles, floods" { + Line_item = class Menuaction "_Line" "draw line on image" { + action x = class + _result { + _vislevel = 3; + + x1 = Expression "Start x" 0; + y1 = Expression "Start y" 0; + x2 = Expression "End x" 100; + y2 = Expression "End y" 100; + + i = Expression "Ink" [0]; + + _result + = map_unary line x + { + line im + = draw_line x1 y1 x2 y2 i.expr im; + } + } + } + + Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { + action x = class + _result { + _vislevel = 3; + + rx = Expression "Left" 50; + ry = Expression "Top" 50; + rw = Expression "Width" 100; + rh = Expression "Height" 100; + + f = Toggle "Fill" true; + + t = Scale "Line thickness" 1 50 3; + + i = Expression "Ink" [0]; + + _result + = map_unary rect x + { + rect im + = draw_rect_width rx ry rw rh f t i.expr im; + } + } + } + + Circle_item = class Menuaction "_Circle" "draw circle on image" { + action x = class + _result { + _vislevel = 3; + + cx = Expression "Centre x" 100; + cy = Expression "Centre y" 100; + r = Expression "Radius" 50; + + f = Toggle "Fill" true; + + i = Expression "Ink" [0]; + + _result + = map_unary circle x + { + circle im + = draw_circle cx cy r f i.expr im; + } + } + } + + Flood_item = class Menuaction "_Flood" "flood bounded area of image" { + action x = class + _result { + _vislevel = 3; + + sx = Expression "Start x" 0; + sy = Expression "Start y" 0; + + e = Option "Flood while" [ + "Not equal to ink", + "Equal to start point" + ] 0; + + // pick a default ink that won't flood, if we can + i + = Expression "Ink" default_ink + { + default_ink + = [0], ! has_image x + = pixel; + pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); + im = get_image x; + } + + _result + = map_unary flood x + { + flood im + = draw_flood sx sy i.expr im, e == 0 + = draw_flood_blob sx sy i.expr im; + } + } + } + + Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { + action x = class + _result { + _vislevel = 3; + + px = Expression "Left" 50; + py = Expression "Top" 50; + wid = Expression "Width" 100; + thick = Scale "Line thickness" 1 50 3; + text = String "Dimension text" "50μm"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + pos = Option "Position Text" ["Above", "Below"] 1; + vp = Option "Dimension by" [ + "Inner Vertical Edge", + "Centre of Vertical", + "Outer Vertical Edge" + ] 1; + dpi = Expression "DPI" 100; + ink = Colour "Lab" [50,0,0]; + + _result + = map_unary process x + { + process im + = blend (Image scale) ink' im + { + // make an ink compatible with the image + ink' = colour_transform_to (get_type im) ink; + + x = to_real px; + y = to_real py; + w = to_real wid; + d = to_real dpi; + + t = floor thick; + + bg = image_new (get_width im) (get_height im) (get_bands im) + (get_format im) (get_coding im) (get_type im) 0 0 0; + draw_block x y w t im = + draw_rect_width x y w t true 1 [255] im; + label = im_text text.value font.value w 1 d; + lw = get_width label; + lh = get_height label; + ly = [y - lh - t, y + 2 * t]?pos; + vx = [ + [x - t, x + w], + [x - t / 2, x + w - t / 2], + [x, x + w - t] + ]?vp; + + scale = (draw_block x y w t @ + draw_block vx?0 (y - 2 * t) t (t * 5) @ + draw_block vx?1 (y - 2 * t) t (t * 5) @ + insert_noexpand (x + w / 2 - lw / 2) ly label) + bg; + } + } + } + } +} + +Image_join_item = class + Menupullright "_Join" "join two or more images together" { + Bandwise_item = class + Menuaction "_Bandwise Join" "join two images bandwise" { + action a b = join a b; + } + + sep1 = Menuseparator; + + join_lr shim bg align a b + = im2 + { + w = a.width + b.width + shim; + h = max_pair a.height b.height; + + back = image_new w h a.bands a.format a.coding a.type bg 0 0; + + ya = [0, max_pair 0 ((b.height - a.height)/2), + max_pair 0 (b.height - a.height)]; + yb = [0, max_pair 0 ((a.height - b.height)/2), + max_pair 0 (a.height - b.height)]; + + im1 = insert_noexpand 0 ya?align a back; + im2 = insert_noexpand (a.width + shim) yb?align b im1; + } + + join_tb shim bg align a b + = im2 + { + w = max_pair a.width b.width; + h = a.height + b.height + shim; + + back = image_new w h a.bands a.format a.coding a.type bg 0 0; + + xa = [0, max_pair 0 ((b.width - a.width)/2), + max_pair 0 (b.width - a.width)]; + xb = [0, max_pair 0 ((a.width - b.width)/2), + max_pair 0 (a.width - b.width)]; + + im1 = insert_noexpand xa?align 0 a back; + im2 = insert_noexpand xb?align (a.height + shim) b im1; + } + + halign_names = ["Top", "Centre", "Bottom"]; + valign_names = ["Left", "Centre", "Right"]; + + Left_right_item = class + Menuaction "_Left to Right" "join two images left-right" { + action a b = class + _result { + _vislevel = 3; + + shim = Scale "Spacing" 0 100 0; + bg_colour = Expression "Background colour" 0; + align = Option "Alignment" halign_names 1; + + _result = map_binary + (join_lr shim.value bg_colour.expr align.value) a b; + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "join two images top-bottom" { + action a b = class + _result { + _vislevel = 3; + + shim = Scale "Spacing" 0 100 0; + bg_colour = Expression "Background colour" 0; + align = Option "Alignment" valign_names 1; + + _result = map_binary + (join_tb shim.value bg_colour.expr align.value) a b; + } + } + + sep2 = Menuseparator; + + Array_item = class + Menuaction "_Array" + "join a list of lists of images into a single image" { + action x = class + _result { + _vislevel = 3; + + hshim = Scale "Horizontal spacing" (-100) (100) 0; + vshim = Scale "Vertical spacing" (-100) (100) 0; + bg_colour = Expression "Background colour" 0; + halign = Option "Horizontal alignment" valign_names 1; + valign = Option "Vertical alignment" halign_names 1; + + // we can't use map_unary since chop-into-tiles returns a group of + // groups and we want to be able to reassemble that + // TODO: chop-into-tiles should return an array class which + // displays as group but does not have the looping behaviour? + _result + = (image_set_origin 0 0 @ + foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ + map (foldl1 (join_lr hshim.value + bg_colour.expr valign.value))) (to_list (to_list x)); + } + } + + ArrayFL_item = class + Menuaction "_Array from List" + "join a list of images into a single image" { + action x = class + _result { + _vislevel = 3; + + ncol = Number "Max. Number of Columns" 1; + hshim = Scale "Horizontal spacing" (-100) (100) 0; + vshim = Scale "Vertical spacing" (-100) (100) 0; + bg_colour = Expression "Background colour" 0; + halign = Option "Horizontal alignment" valign_names 1; + valign = Option "Vertical alignment" halign_names 1; + snake = Toggle "Reverse the order of every other row" false; + + _l + = split_lines ncol.value x.value, is_Group x + = split_lines ncol.value x; + + _l' + = map2 reverse_if_odd [0..] _l, snake + = _l + { + reverse_if_odd n x + = reverse x, n % 2 == 1 + = x; + } + + _result + = (image_set_origin 0 0 @ + foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ + map (foldl1 (join_lr hshim.value + bg_colour.expr valign.value))) (to_list (to_list _l')); + } + } +} + +Image_tile_item = class + Menupullright "Til_e" "tile an image across and down" { + tile_widget default_type x = class + _result { + _vislevel = 3; + + across = Expression "Tiles across" 2; + down = Expression "Tiles down" 2; + repeat = Option "Tile type" + ["Replicate", "Four-way mirror"] default_type; + + _result + = map_unary process x + { + process image + = tile across down image, repeat == 0 + = tile across down image'' + { + image' = insert image.width 0 (fliplr image) image; + image'' = insert 0 image.height (fliptb image') image'; + } + } + } + + Replicate_item = class + Menuaction "_Replicate" "replicate image across and down" { + action x = tile_widget 0 x; + } + + Fourway_item = class + Menuaction "_Four-way Mirror" "four-way mirror across and down" { + action x = tile_widget 1 x; + } + + Chop_item = class + Menuaction "_Chop Into Tiles" "slice an image into tiles" { + action x = class + _result { + _vislevel = 3; + + tile_width = Expression "Tile width" 100; + tile_height = Expression "Tile height" 100; + hoverlap = Expression "Horizontal overlap" 0; + voverlap = Expression "Vertical overlap" 0; + + _result + = map_unary (Group @ map Group @ process) x + { + process x + = imagearray_chop tile_width tile_height + hoverlap voverlap x; + } + } + } +} + +#separator + +Pattern_images_item = class + Menupullright "_Patterns" "make a variety of useful patterns" { + Grey_item = class + Menuaction "Grey _Ramp" "make a smooth grey ramp" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + orientation = Option "Orientation" [ + "Horizontal", + "Vertical" + ] 0; + foption = Option "Format" ["8 bit", "float"] 0; + + _result + = Image im + { + gen + = im_grey, foption == 0 + = im_fgrey; + w = to_real nwidth; + h = to_real nheight; + im + = gen w h, orientation == 0 + = rot90 (gen h w); + } + } + } + + Xy_item = class + Menuaction "_XY Image" + "make a two band image whose pixel values are their coordinates" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + + _result = Image (make_xy nwidth nheight); + } + } + + Noise_item = class + Menupullright "_Noise" "various noise generators" { + Gaussian_item = class + Menuaction "_Gaussian" "make an image of gaussian noise" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + mean = Scale "Mean" 0 255 128; + deviation = Scale "Deviation" 0 128 50; + + _result = Image (gaussnoise nwidth nheight + mean.value deviation.value); + } + } + + Fractal_item = class + Menuaction "_Fractal" "make a fractal noise image" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + dimension = Scale "Dimension" 2.001 2.999 2.001; + + _result = Image (im_fractsurf (to_real nsize) dimension.value); + } + } + + Perlin_item = class + Menuaction "_Perlin" "Perlin noise image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + cell_size = Expression "Cell size (pixels)" 8; + eight = Toggle "Eight bit output" true; + + _result + = 128 * im + 128, eight + = im + { + im = perlin cell_size nwidth nheight; + } + } + } + + Worley_item = class + Menuaction "_Worley" "Worley noise image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 512; + nheight = Expression "Image height (pixels)" 512; + cell_size = Expression "Cell size (pixels)" 256; + + _result + = worley cell_size nwidth nheight; + } + } + + } + + Checkerboard_item = class + Menuaction "_Checkerboard" "make a checkerboard image" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + hpsize = Expression "Horizontal patch size" 8; + vpsize = Expression "Vertical patch size" 8; + hpoffset = Expression "Horizontal patch offset" 0; + vpoffset = Expression "Vertical patch offset" 0; + + _result + = Image (xstripes ^ ystripes) + { + pixels = make_xy nwidth nheight; + xpixels = pixels?0 + to_real hpoffset; + ypixels = pixels?1 + to_real vpoffset; + + make_stripe pix swidth = pix % (swidth * 2) >= swidth; + + xstripes = make_stripe xpixels (to_real hpsize); + ystripes = make_stripe ypixels (to_real vpsize); + } + } + } + + Grid_item = class + Menuaction "Gri_d" "make a grid" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + hspace = Expression "Horizontal line spacing" 8; + vspace = Expression "Vertical line spacing" 8; + thick = Expression "Line thickness" 1; + hoff = Expression "Horizontal grid offset" 4; + voff = Expression "Vertical grid offset" 4; + + _result + = Image (xstripes | ystripes) + { + pixels = make_xy nwidth nheight; + xpixels = pixels?0 + to_real hoff; + ypixels = pixels?1 + to_real voff; + + make_stripe pix swidth = pix % swidth < to_real thick; + + xstripes = make_stripe xpixels (to_real hspace); + ystripes = make_stripe ypixels (to_real vspace); + } + } + } + + Text_item = class + Menuaction "_Text" "make a bitmap of some text" { + action = class + _result { + _vislevel = 3; + + text = String "Text to paint" "Hello world!"; + font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; + wrap = Expression "Wrap text at" 500; + align = Option "Alignment" [ + "Left", + "Centre", + "Right" + ] 0; + dpi = Expression "DPI" 300; + + _result = Image (im_text text.value font.value + (to_real wrap) align.value (to_real dpi)); + } + } + + New_CIELAB_slice_item = class + Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + L = Scale "L value" 0 100 50; + + _result = Image (lab_slice (to_real nsize) L.value); + } + } + + sense_option = Option "Sense" [ + "Pass", + "Reject" + ] 0; + + build fn size + = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) + (im_create_fmask size size); + + New_ideal_item = class + Menupullright "_Ideal Fourier Mask" + "make various ideal Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f sense.value fc.value 0 0 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 6) fc.value rw.value 0 0 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "ideal Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 12) fcx.value fcy.value + r.value 0 0; + } + } + } + } + + New_gaussian_item = class + Menupullright "_Gaussian Fourier Mask" + "make various Gaussian Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 4) fc.value ac.value 0 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 10) fc.value rw.value + ac.value 0 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "Gaussian Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 16) fcx.value fcy.value + r.value ac.value 0; + } + } + } + } + + New_butterworth_item = class + Menupullright "_Butterworth Fourier Mask" + "make various Butterworth Fourier filter masks" { + High_low_item = class + Menuaction "_High or Low Pass" + ("make a mask image for a highpass/lowpass " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 2) order.value fc.value + ac.value 0 0; + } + } + } + + Ring_item = class + Menuaction "_Ring Pass or Ring Reject" + ("make a mask image for an ring pass/reject " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fc = Scale "Frequency cutoff" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + rw = Scale "Ring width" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 8) order.value fc.value + rw.value ac.value 0; + } + } + } + + Band_item = class + Menuaction "_Band Pass or Band Reject" + ("make a mask image for a band pass/reject " ++ + "Butterworth Fourier filter") { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + sense = sense_option; + fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; + fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; + r = Scale "Radius" 0.01 0.99 0.5; + ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; + order = Scale "Order" 1 10 2; + + _result + = build param (to_real nsize) + { + param f = f (sense.value + 14) order.value fcx.value + fcy.value r.value ac.value; + } + } + } + } +} + +Test_images_item = class + Menupullright "Test I_mages" "make a variety of test images" { + Eye_item = class + Menuaction "_Spatial Response" + "image for testing the eye's spatial response" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + nheight = Expression "Image height (pixels)" 64; + factor = Scale "Factor" 0.001 1 0.2; + + _result = Image (im_eye (to_real nwidth) (to_real nheight) + factor.value); + } + } + + Zone_plate = class + Menuaction "_Zone Plate" "make a zone plate" { + action = class + _result { + _vislevel = 3; + + nsize = Expression "Image size (pixels)" 64; + + _result = Image (im_zone (to_real nsize)); + } + } + + Frequency_test_chart_item = class + Menuaction "_Frequency Testchart" + "make a black/white frequency test pattern" { + action = class + _result { + _vislevel = 3; + + nwidth = Expression "Image width (pixels)" 64; + sheight = Expression "Strip height (pixels)" 10; + waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; + + _result + = imagearray_assemble 0 0 (transpose [strips]) + { + freq_slice wave = Image (sin (grey / wave) > 0); + strips = map freq_slice waves.expr; + grey = im_fgrey (to_real nwidth) (to_real sheight) * + 360 * (to_real nwidth); + } + } + } + + CRT_test_chart_item = class + Menuaction "CRT _Phosphor Chart" + "make an image for measuring phosphor colours" { + action = class + _result { + _vislevel = 3; + + brightness = Scale "Brightness" 0 255 200; + psize = Expression "Patch size (pixels)" 32; + + _result + = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) + { + + black = image_new (to_real psize) (to_real psize) 1 + Image_format.FLOAT Image_coding.NOCODING + Image_type.B_W 0 0 0; + notblack = black + brightness; + + green = black ++ notblack ++ black; + red = notblack ++ black ++ black; + blue = black ++ black ++ notblack; + white = notblack ++ notblack ++ notblack; + } + } + } + + Greyscale_chart_item = class + Menuaction "_Greyscale" "make a greyscale" { + action = class + _result { + _vislevel = 3; + + pwidth = Expression "Patch width" 8; + pheight = Expression "Patch height" 8; + npatches = Expression "Number of patches" 16; + + _result + = Image (image_set_type Image_type.B_W + (clip2fmt Image_format.UCHAR wedge)) + { + wedge + = 255 / (to_real npatches - 1) * + (int) (strip?0 / to_real pwidth) + { + strip = make_xy (to_real pwidth * to_real npatches) pheight; + } + } + } + } + + CMYK_test_chart_item = class + Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { + action = class + _result { + _vislevel = 3; + + pwidth = Expression "Patch width" 8; + pheight = Expression "Patch height" 8; + npatches = Expression "Number of patches" 16; + + _result + = Image (image_set_type Image_type.CMYK + (clip2fmt Image_format.UCHAR strips)) + { + wedge + = 255 / (to_real npatches - 1) * + (int) (strip?0 / to_real pwidth) + { + strip = make_xy (to_real pwidth * to_real npatches) pheight; + } + + black = wedge * 0; + + C = wedge ++ black ++ black ++ black; + M = black ++ wedge ++ black ++ black; + Y = black ++ black ++ wedge ++ black; + K = black ++ black ++ black ++ wedge; + + strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; + } + } + } + + Colour_atlas_item = class + Menuaction "_Colour Atlas" + "make a grid of patches grouped around a colour" { + action = class + _result { + _vislevel = 3; + + start = Colour_picker "Lab" [50,0,0]; + nstep = Expression "Number of steps" 9; + ssize = Expression "Step size" 10; + psize = Expression "Patch size" 32; + sepsize = Expression "Separator size" 4; + + _result + = colour_transform_to (get_type start) blocks''' + { + size = (to_real nstep * 2 + 1) * to_real psize - + to_real sepsize; + xy = make_xy size size; + + xy_grid = (xy % to_real psize) < + (to_real psize - to_real sepsize); + grid = xy_grid?0 & xy_grid?1; + + blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); + lab_start = colour_transform_to Image_type.LAB start; + blocks' = blocks - to_real nstep * to_real ssize + + Vector (tl lab_start.value); + blocks'' = hd lab_start.value ++ Image blocks'; + blocks''' + = image_set_type Image_type.LAB blocks'', Image grid + = 0; + } + } + } +} + diff --git a/share/nip2/compat/8.6/Magick.def b/share/nip2/compat/8.6/Magick.def new file mode 100644 index 00000000..ab6cdb35 --- /dev/null +++ b/share/nip2/compat/8.6/Magick.def @@ -0,0 +1,2423 @@ +/* + + ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). + 1-Apr-2014 + Minor corrections to Geometry_widget and Alpha. + Added loads of widgets and Menuactions. + Not fully tested. + 5-Apr-2014 + Many more menu actions. + Reorganised Magick menu. + 10-Apr-2014 + Many more menu actions. + 11-Apr-2014 jcupitt + Split to separate Magick.def + 13-Apr-2014 snibgo + Put "new image" items into sub-menu. + New class VirtualPixlBack. + 17-Apr-2014 snibgo + Many small changes. + A few new menu options. + Created sub-menu for multi-input operations. + 3-May-2014 jcupitt + Put quotes around ( in shadow to help unix + + Last update: 17-Apr-2014. + + For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. +*/ + +// We don't need Noop. +/*=== +Magick_noop_item = class + Menuaction "_Noop" "no operation" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} +===*/ + +Magick_testPar_item = class + Menuaction "_TestPar" "no operation" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "( +clone ) +append ", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +/* Removed Read_item and Write_item, much better to use nip2 load/save image. + * Plus they can load all libMagick formats anyway. + */ + + +// Put "new image" items into sub-menu +Magick_NewImageMenu_item = class + Menupullright "_New image" "make a new image" { + + Magick_newcanvas_item = class + Menuaction "_Solid colour" "make image of solid colour" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + colour = Magick.generalcol_widget; + command = Magick.command [ + size._flag, + "\"canvas:" ++ colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_builtin_item = class + Menuaction "_Built-in image" "create a built-in image" { + action = class + _result { + _vislevel = 3; + + builtin = Magick.builtin_widget; + command = Magick.command [ + builtin._flag, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_gradient_item = class + Menuaction "_Gradient" "make a linear gradient between two colours" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + topColour = Magick.generalcol_widget; + bottomColour = Magick.generalcol_widget; + + command = Magick.command [ + size._flag, + concat ["\"gradient:", + topColour._flag, "-", bottomColour._flag, "\""], + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_hald_item = class + Menuaction "_Hald-clut image" "create an identity hald-clut image" { + action = class + _result { + _vislevel = 3; + + order = Expression "order" 8; + command = Magick.command [ + "hald:" ++ print order.expr, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_pattern_item = class + Menuaction "_Pattern" "create pattern" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + pattern = Magick.pattern_widget; + command = Magick.command [ + size._flag, + pattern._flag, + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_plasma_item = class + Menuaction "_Plasma image" "create plasma image" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + // FIXME? ColourA-ColourB. + // FIXME? Allow plasma:fractal? + + command = Magick.command [ + size._flag, + "plasma:", + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } + + Magick_radialgradient_item = class + Menuaction "_Radial gradient" + "make a radial gradient between two colours" { + action = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + innerColour = Magick.generalcol_widget; + outerColour = Magick.generalcol_widget; + + command = Magick.command [ + size._flag, + concat ["\"radial-gradient:", + innerColour._flag, "-", outerColour._flag, "\""], + "\"%s\"" + ]; + + _result = Magick.system0 command; + } + } +} // end Magick_NewImageMenu_item + + +Magick_MultiMenu_item = class + Menupullright "_Multiple inputs" "make an image from multiple images" { + + Magick_composite_item = class + Menuaction "_Composite" "composite two images (without mask)" { + action x y = class + _result { + _vislevel = 3; + + method = Magick.compose_widget; + offsets = Magick.OffsetGeometry_widget; + gravity = Magick.gravity_widget; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-geometry", offsets._flag, + gravity._flag, + method._flag, + "-composite", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + Magick_compositeMask_item = class + Menuaction "_Composite masked" "composite two images (with mask)" { + action x y z = class + _result { + _vislevel = 3; + + method = Magick.compose_widget; + offsets = Magick.OffsetGeometry_widget; + gravity = Magick.gravity_widget; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "\"%s\"", + "-geometry", offsets._flag, + gravity._flag, + method._flag, + "-composite", + "\"%s\"" + ]; + + _result = Magick.system3 command x y z; + } + } + + // FIXME: other operations like remap that take another image as arguments are: + // mask (pointless?), texture, tile (pointless?) + + // FIXME: operations that take a filename that isn't an image: + // cdl, profile + + Magick_clut_item = class + Menuaction "_Clut" "replace values using second image as colour look-up table" { + action x y = class + _result { + _vislevel = 3; + + // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-clut", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + Magick_haldclut_item = class + Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { + action x y = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "\"%s\"", + "-hald-clut", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + + + // Encipher and decipher: key files can be text or image files. + + Magick_encipher_item = class + Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { + action x = class + _result { + _vislevel = 3; + + pathname = Pathname "Read key file" ""; + isDecipher = Toggle "Decipher" false; + + command = Magick.command [ + "\"%s\"", + if pathname.value == "" then "" else ( + ( if isDecipher then "-decipher " else "-encipher ") ++ + ( "\"" ++ pathname.value ++ "\"" ) + ), + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_profile_item = class + Menuaction "_Profile" "assigns/applies an ICC profile" { + action x = class + _result { + _vislevel = 3; + + pathname1 = Pathname "Read profile file" ""; + pathname2 = Pathname "Read profile file" ""; + + command = Magick.command [ + "\"%s\"", + if pathname1.value == "" then "" else ( + "-profile " ++ + "\"" ++ pathname1.value ++ "\""), + if pathname2.value == "" then "" else ( + "-profile " ++ + "\"" ++ pathname2.value ++ "\""), + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_remap_item = class + Menuaction "_Remap" "reduce colours to those in another image" { + action x y = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-remap", + "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system2 command x y; + } + } + +} // end Magick_MultiMenu_item + + +Magick_image_type_item = class + Menuaction "_Image Type" "change image type" { + action x = class + _result { + _vislevel = 3; + + imagetype = Magick.imagetype_widget; + + command = Magick.command [ + "\"%s\"", + imagetype._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +sep2 = Menuseparator; + +Magick_alpha_item = class + Menuaction "_Alpha" "add/remove alpha channel" { + action x = class + _result { + _vislevel = 3; + + alpha = Magick.alpha_widget; + command = Magick.command [ + "\"%s\"", + alpha._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_annotate_item = class + Menuaction "_Annotate" "add text annotation" { + action x = class + _result { + _vislevel = 3; + + text = Magick.text_widget; + font = Magick.Font_widget; + geometry = Magick.AnnotGeometry_widget; + gravity = Magick.gravity_widget; + foreground = Magick.foreground_widget; + undercol = Magick.undercol_widget; + antialias = Magick.antialias_widget; + command = Magick.command [ + "\"%s\"", + font._flag, + antialias._flag, + gravity._flag, + foreground._flag, + undercol._flag, + "-annotate", + geometry._flag, + "\"" ++ text.value ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_autoGamma_item = class + Menuaction "_AutoGamma" "automatic gamma" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + command = Magick.command [ + "\"%s\"", + channels._flag, + "-auto-gamma", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_autoLevel_item = class + Menuaction "_AutoLevel" "automatic level" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + command = Magick.command [ + "\"%s\"", + channels._flag, + "-auto-level", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_blurSharpMenu_item = class + Menupullright "_Blur/Sharpen" "blur and sharpen" { + + Magick_adaptive_blur_item = class + Menuaction "_Adaptive Blur" "blur less near edges" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + // note: adaptive-blur doesn't regard VP. + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-adaptive-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_blur_item = class + Menuaction "_Blur" "blur" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_gaussianBlur_item = class + Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-gaussian-blur", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_motionBlur_item = class + Menuaction "_Motion Blur" "simulate motion blur" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + angle = Scale "angle" (-360) 360 0; + channels = Magick.ch_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-motion-blur", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_rotationalBlur_item = class + Menuaction "_RotationalBlur" "blur around the centre" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + angle = Scale "angle (degrees)" (-360) 360 20; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-radial-blur", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_selectiveBlur_item = class + Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + intensity._flag, + virtpixback._flag, + channels._flag, + "-selective-blur", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print threshold.value ++ "%%", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + sep1 = Menuseparator; + + Magick_adaptive_sharpen_item = class + Menuaction "_Adaptive Sharpen" "sharpen more near edges" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-adaptive-sharpen", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_sharpen_item = class + Menuaction "_Sharpen" "sharpen" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + virtpixback = Magick.VirtualPixelBack_widget; + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-sharpen", + print radius.value ++ "x" ++ print sigma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_unsharpen_item = class + Menuaction "_Unsharp" "sharpen with unsharp mask" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + channels = Magick.channels_widget; + gain = Scale "Gain" (-10) 10 1; + threshold = Scale "Threshold" 0 1 0.05; + virtpixback = Magick.VirtualPixelBack_widget; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + channels._flag, + "-unsharp", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print gain.value ++ "+" ++ + print threshold.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end BlurSharpMenu_item + + +Magick_border_item = class + Menuaction "_Border" "add border of given colour" { + action x = class + _result { + _vislevel = 3; + + compose = Magick.compose_widget; + width = Expression "Width" 3; + bordercol = Magick.bordercol_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + bordercol._flag, + "-border", + print width.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_brightCont_item = class + Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { + action x = class + _result { + _vislevel = 3; + + bri = Scale "brightness" (-100) 100 0; + con = Scale "contrast" (-100) 100 0; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-brightness-contrast", + print bri.value ++ "x" ++ print con.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// Note: canny requires ImageMagick 6.8.9-0 or later. + +Magick_canny_item = class + Menuaction "_Canny" "detect a wide range of edges" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + lowPc = Scale "lower percent" 0 100 10; + highPc = Scale "lower percent" 0 100 10; + + command = Magick.command [ + "\"%s\"", + "-canny", + concat ["\"", + print radius.value ++ "x" ++ + print sigma.value ++ "+" ++ + print lowPc.value ++ "%%+" ++ + print highPc.value ++ "%%" ++ "\"" + ], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + +Magick_charcoal_item = class + Menuaction "_Charcoal" "simulate a charcoal drawing" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + factor = Scale "factor" 0 50 1; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-charcoal", + print factor.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_chop_item = class + Menuaction "_Chop" "remove pixels from the interior" { + action x = class + _result { + _vislevel = 3; + + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + gravity._flag, + "-chop", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colorize_item = class + Menuaction "_Colorize" "colorize by given amount" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + val = Scale "value" 0 100 100; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-colorize", + print val.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colors_item = class + Menuaction "_Colors" "reduce number of colors" { + action x = class + _result { + _vislevel = 3; + + treedepth = Expression "Treedepth" 8; + dither = Magick.dither_widget; + quantize = Magick.colorspace_widget; + colors = Expression "Colours" 3; + + command = Magick.command [ + "\"%s\"", + "-quantize", quantize._flag, + "-treedepth", + print treedepth.expr, + dither._flag, + "-colors", + print colors.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: color-matrix? + +Magick_colorspace_item = class + Menuaction "_Colourspace" "convert to arbitrary colourspace" { + action x = class + _result { + _vislevel = 3; + + colsp = Magick.colorspace_widget; + + command = Magick.command [ + "\"%s\"", + "-colorspace", + colsp._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_colorspaceGray_item = class + Menuaction "_Colourspace gray" "convert to gray using given intensity method" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + "-colorspace gray", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_contrast_item = class + Menuaction "_Contrast" "increase or reduce the contrast" { + action x = class + _result { + _vislevel = 3; + + isReduce = Toggle "reduce contrast" false; + + command = Magick.command [ + "\"%s\"", + (if isReduce then "+" else "-") ++ "contrast", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_contrastStretch_item = class + Menuaction "_Contrast stretch" "stretches tones, making some black/white" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + blk = Scale "percent to make black" 0 100 0; + wht = Scale "percent to make white" 0 100 0; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-contrast-stretch", + "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: convolve (bias, kernel) + +Magick_crop_item = class + Menuaction "_Crop" "cut out a rectangular region" { + action x = class + _result { + _vislevel = 3; + + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + gravity._flag, + "-crop", + geometry._flag, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_deskew_item = class + Menuaction "_Deskew" "straighten the image" { + action x = class + _result { + _vislevel = 3; + + threshold = Scale "Threshold (percent)" 0 100 80; + + // FIXME: toggle auto-crop? + + command = Magick.command [ + "\"%s\"", + "-deskew", + "\"" ++ print threshold.value ++ "%%\"", + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_despeckle_item = class + Menuaction "_Despeckle" "reduce the speckles" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-despeckle", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_distort_item = class + Menuaction "_Distort" "distort using a method and arguments" { + action x = class + _result { + _vislevel = 3; + + virtpixback = Magick.VirtualPixelBack_widget; + distort = Magick.distort_widget; + args = String "Arguments" "1,0"; + isPlus = Toggle "Extend to show entire image" false; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + (if isPlus then "+" else "-") ++ "distort", + distort._flag, + args.value, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_draw_item = class + Menuaction "_Draw" "annotate with one or more graphic primitives" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-draw", + concat ["\"", args.value, "\""], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_edge_item = class + Menuaction "_Edge" "detect edges" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "Radius" 3; + + command = Magick.command [ + "\"%s\"", + "-edge", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_emboss_item = class + Menuaction "_Emboss" "emboss" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "Radius" 3; + + command = Magick.command [ + "\"%s\"", + "-emboss", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_enhance_item = class + Menuaction "_Enhance" "enhance a noisy image" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-enhance", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_equalize_item = class + Menuaction "_Equalize" "equalize the histogram" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-equalize", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_evaluate_item = class + Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + operation = Magick.evaluate_widget; + val = Expression "value" 5; + isPc = Toggle "Value is percent" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + operation._flag, + print val.expr ++ if isPc then "%%" else "", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_extent_item = class + Menuaction "_Extent" "set the image size and offset" { + action x = class + _result { + _vislevel = 3; + + background = Magick.background_widget; + compose = Magick.compose_widget; + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + background._flag, + gravity._flag, + "-extent", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + +Magick_FlipFlopMenu_item = class + Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { + + Magick_flip_item = class + Menuaction "_Flip vertically" "mirror upside-down" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-flip", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_flop_item = class + Menuaction "_Flop horizontally" "mirror left-right" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-flop", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_transpose_item = class + Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-transpose +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_transverse_item = class + Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-transverse +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end Magick_FlipFlopMenu_item + + +Magick_floodfill_item = class + Menuaction "_Floodfill" "recolour neighbours that match" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + fuzz = Magick.fuzz_widget; + coordinate = Magick.coordinate_widget; + + // -draw "color x,y floodfill" + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-fuzz", + "\"" ++ print fuzz.value ++ "%%\"", + "-draw \" color", + coordinate._flag, + "floodfill \"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_frame_item = class + Menuaction "_Frame" "surround with border or beveled frame" { + action x = class + _result { + _vislevel = 3; + + border = Magick.bordercol_widget; + compose = Magick.compose_widget; + matte = Magick.mattecol_widget; + geometry = Magick.FrameGeometry_widget; + + command = Magick.command [ + "\"%s\"", + compose._flag, + border._flag, + matte._flag, + "-frame", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_function_item = class + Menuaction "_Function" "evaluate a function on each pixel channel" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + function = Magick.function_widget; + // FIXME: explain values; use sensible defaults. + values = String "values" "0,0,0,0"; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-function", + function._flag, + values.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_fx_item = class + Menuaction "_Fx" "apply a mathematical expression" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + interpolate = Magick.interpolate_widget; + virtpixback = Magick.VirtualPixelBack_widget; + args = String "Expression" "u*1/2"; + + command = Magick.command [ + "\"%s\"", + channels._flag, + interpolate._flag, + virtpixback._flag, + "-fx", + concat ["\"", args.value, "\""], + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gamma_item = class + Menuaction "_Gamma" "apply a gamma correction" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + gamma = Magick.gamma_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-gamma", + print gamma.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gradient_item = class + Menuaction "_Gradient" "apply a linear gradient" { + action x = class + _result { + _vislevel = 3; + + colourA = Magick.generalcol_widget; + colourB = Magick.generalcol_widget; + + position = Option "colourA is at" [ + "top", "bottom", + "left", "right", + "top-left", "top-right", + "bottom-left", "bottom-right"] 0; + _baryArg + = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 + = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 + = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 + = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 + = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 + = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 + = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 + = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 + = "dunno"; + + command = Magick.command [ + "\"%s\"", + "-sparse-color barycentric \"" ++ _baryArg ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_gradientCorn_item = class + Menuaction "_Gradient corners" + "apply a bilinear gradient between the corners" { + action x = class + _result { + _vislevel = 3; + + colour_top_left = Magick.generalcol_widget; + colour_top_right = Magick.generalcol_widget; + colour_bottom_left = Magick.generalcol_widget; + colour_bottom_right = Magick.generalcol_widget; + + command = Magick.command [ + "\"%s\"", + "-sparse-color bilinear \"" ++ + "0,0," ++ colour_top_left._flag ++ + ",%%[fx:w-1],0" ++ colour_top_right._flag ++ + ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ + ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", + "+depth", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_histogram_item = class + Menuaction "_Histogram" "make a histogram image" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-define histogram:unique-colors=false histogram:" ++ + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_implode_item = class + Menuaction "_Implode" "implode pixels about the center" { + action x = class + _result { + _vislevel = 3; + + factor = Scale "factor" 0 20 1; + interpolate = Magick.interpolate_widget; + // FIXME: virtual-pixel? + + command = Magick.command [ + "\"%s\"", + interpolate._flag, + "-implode", + print factor.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_level_item = class + Menuaction "_Level" "adjust the level of channels" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + blk = Scale "black point" (-100) 200 0; + wht = Scale "white point" (-100) 200 100; + gam = Scale "gamma" 0 30 1; + isPc = Toggle "Levels are percent" true; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "level", + "\"" ++ print blk.value ++ "," ++ + print wht.value ++ (if isPc then "%%" else "") ++ "," ++ + print gam.value ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_levelCols_item = class + Menuaction "_Level colors" "adjust levels to given colours" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + colour_black = Magick.generalcol_widget; + colour_white = Magick.generalcol_widget; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "level-colors", + "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_linearStretch_item = class + Menuaction "_Linear stretch" "stretches tones, making some black/white" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + blk = Scale "percent to make black" 0 100 0; + wht = Scale "percent to make white" 0 100 0; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-linear-stretch", + "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_magnify_item = class + Menuaction "_Magnify" "double the size of the image with pixel art scaling" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-magnify", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_modulate_item = class + Menuaction "_Modulate" "modulate brightness, saturation and hue" { + action x = class + _result { + _vislevel = 3; + + modcolsp = Magick.ModColSp_widget; + bright = Scale "brightness" 0 200 100; + sat = Scale "saturation" 0 200 100; + hue = Scale "hue" 0 200 100; + + command = Magick.command [ + "\"%s\"", + modcolsp._flag, + "-modulate", + print bright.value ++ "," ++ print sat.value ++ "," ++ + print hue.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_monochrome_item = class + Menuaction "_Monochrome" "transform to black and white" { + action x = class + _result { + _vislevel = 3; + + // FIXME: also intensity? + + intensity = Magick.intensity_widget; + treedepth = Expression "Treedepth" 8; + dither = Magick.dither_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + dither._flag, + "-treedepth", + print treedepth.expr, + "-monochrome", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_morphology_item = class + // See http://www.imagemagick.org/Usage/morphology/ + Menuaction "_Morphology" "apply a morphological method" { + action x = class + _result { + _vislevel = 3; + + method = Magick.morphmeth_widget; + iter = Expression "Iterations (-1=repeat until done)" 1; + + kernel = Magick.kernel_widget; + // FIXME: custom kernel eg "3x1+2+0:1,0,0" + // width x height + offsx + offsy : {w*h values} + // each value is 0.0 to 1.0 or "NaN" or "-" + + // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 + // but + // ring takes: radius1, radius2, scale + // rectangle and comet take: width x height + offsx + offsy + // blur takes: radius x sigma + // FIXME: for now, simply allow any string input. + kernel_arg = String "Kernel arguments" ""; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-morphology", + method._flag ++ ":" ++ print iter.expr, + kernel._flag ++ + (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_negate_item = class + Menuaction "_Negate" "negate" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-negate", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_addNoise_item = class + Menuaction "_add Noise" "add noise" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + attenuate = Scale "attenuate" 0 1.0 1.0; + noise = Magick.noise_widget; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-attenuate", + print attenuate.value, + noise._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_normalize_item = class + Menuaction "_Normalize" "normalize" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-normalize", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_opaque_item = class + Menuaction "_Opaque" "change this colour to the fill colour" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + fill = Magick.foreground_widget; + changeColour = Magick.changeCol_widget; + + command = Magick.command [ + "\"%s\"", + fill._flag, + channels._flag, + "-fuzz", + "\"" ++ print changeColour.fuzz.value ++ "%%\"", + (if changeColour.nonMatch then "+" else "-") ++ "opaque", + "\"" ++ changeColour.colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_paint_item = class + Menuaction "_Paint" "simulate an oil painting" { + action x = class + _result { + _vislevel = 3; + + rad = Expression "radius" 10; + + command = Magick.command [ + "\"%s\"", + "-paint", + print rad.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +/*=== FIXME Bug; remove for now. +Polaroid_item = class + Menuaction "_Polaroid" "simulate a polaroid picture" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "angle" (-90) 90 20; + + command = Magick.command [ + "\"%s\"", + "-polaroid", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} +===*/ + +Magick_posterize_item = class + Menuaction "_Posterize" "reduce to (n) levels per channel" { + action x = class + _result { + _vislevel = 3; + + levels = Expression "levels" 3; + + command = Magick.command [ + "\"%s\"", + "-posterize", + print levels.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_raise_item = class + Menuaction "_Raise" "lighten or darken image edges" { + action x = class + _result { + _vislevel = 3; + + thk = Expression "Thickness" 3; + + command = Magick.command [ + "\"%s\"", + "-raise", + print thk.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_resize_item = class + Menuaction "_Resize" "resize to given width and height" { + action x = class + _result { + _vislevel = 3; + + filter = Magick.filter_widget; + type = Magick.ResizeType_widget; + width = Scale "Width" 1 100 10; + height = Scale "Height" 1 100 10; + isPc = Toggle "Width and height are percent" false; + + command = Magick.command [ + "\"%s\"", + filter._flag, + "-" ++ type._flag, + "\"" ++ print width.value ++ "x" ++ print height.value ++ + (if isPc then "%%" else "!") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_roll_item = class + Menuaction "_Roll" "roll an image horizontally or vertically" { + action x = class + _result { + _vislevel = 3; + + rollx = Expression "X" 3; + rolly = Expression "Y" 3; + + command = Magick.command [ + "\"%s\"", + "-roll", + (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ + (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_rotate_item = class + Menuaction "_Rotate" "rotate" { + action x = class + _result { + _vislevel = 3; + + angle = Scale "angle (degrees)" (-360) 360 20; + virtpixback = Magick.VirtualPixelBack_widget; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + "+distort", + "SRT", + print angle.value, + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: -segment, but cluster-threshold should be percentage of image area. + +Magick_sepia_item = class + Menuaction "_Sepia tone" "simulate a sepia-toned photo" { + action x = class + _result { + _vislevel = 3; + + threshold = Scale "Threshold (percent)" 0 100 80; + + command = Magick.command [ + "\"%s\"", + "-sepia-tone", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shade_item = class + Menuaction "_Shade" "shade with a distant light source" { + action x = class + _result { + _vislevel = 3; + + azimuth = Scale "Azimuth (degrees)" (-360) 360 0; + elevation = Scale "Elevation (degrees)" 0 90 45; + + command = Magick.command [ + "\"%s\"", + "-shade", + print azimuth.value ++ "x" ++ print elevation.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shadow_item = class + Menuaction "_Shadow" "simulate a shadow" { + action x = class + _result { + _vislevel = 3; + + shadowCol = Magick.generalcol_widget; + opacity = Scale "Opacity (percent)" 0 100 75; + sigma = Scale "Sigma" 0 30 2; + // FIXME: make offsets a single widget? + offsx = Scale "X-offset" (-20) 20 4; + offsy = Scale "Y-offset" (-20) 20 4; + arePc = Toggle "offsets are percentages" false; + + // FIXME: raw operation creates page offset, which vips dislikes. + // So we take this futher, compositing with source. + command = Magick.command [ + "\"%s\"", + "\"(\" +clone", + "-background", "\"" ++ shadowCol._flag ++ "\"", + "-shadow", + concat [ + "\"", + print opacity.value, "x", print sigma.value, + (if offsx.value >= 0 then "+" else ""), print offsx.value, + (if offsy.value >= 0 then "+" else ""), print offsy.value, + (if arePc then "%%" else ""), + "\"" + ], + "\")\" +swap -background None -layers merge", + "+repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shave_item = class + Menuaction "_Shave" "shave pixels from the edges" { + action x = class + _result { + _vislevel = 3; + + width = Scale "Width" 0 50 10; + height = Scale "Height" 0 50 10; + isPc = Toggle "Width and height are percent" false; + + command = Magick.command [ + "\"%s\"", + "-shave", + "\"" ++ print width.value ++ "x" ++ print height.value ++ + (if isPc then "%%" else "") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_shear_item = class + Menuaction "_Shear" "shear along the x-axis and/or y-axis" { + action x = class + _result { + _vislevel = 3; + + shearX = Expression "shear X (degrees)" 0; + shearY = Expression "shear Y (degrees)" 0; + background = Magick.background_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + "-shear", + print shearX.expr ++ "x" ++ print shearY.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_sigmoid_item = class + Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + contrast = Scale "contrast" 0 30 3; + midpoint = Scale "mid-point (percent)" 0 100 50; + isInv = Toggle "Invert effect" false; + + command = Magick.command [ + "\"%s\"", + channels._flag, + (if isInv then "+" else "-") ++ "sigmoidal-contrast", + "\"" ++ print contrast.value ++ "x" ++ + print midpoint.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_sketch_item = class + Menuaction "_Sketch" "simulate a pencil sketch" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + angle = Scale "angle" (-360) 360 0; + + command = Magick.command [ + "\"%s\"", + "-sketch", + print radius.value ++ "x" ++ print sigma.value ++ + (if angle >= 0 then ("+" ++ print angle.value) else ""), + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_solarize_item = class + Menuaction "_Solarize" "negate all pixels above a threshold level" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-solarize", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +// FIXME: -sparse-color needs abitrary list of {x,y,colour}. + +Magick_splice_item = class + Menuaction "_Splice" "splice a colour into the image" { + action x = class + _result { + _vislevel = 3; + + background = Magick.background_widget; + gravity = Magick.gravity_widget; + geometry = Magick.WhxyGeometry_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + gravity._flag, + "-splice", + geometry._flag, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_spread_item = class + Menuaction "_Spread" "displace pixels by random amount" { + action x = class + _result { + _vislevel = 3; + + virtpixback = Magick.VirtualPixelBack_widget; + amount = Expression "Amount (pixels)" 5; + + command = Magick.command [ + "\"%s\"", + virtpixback._flag, + "-spread", + print amount.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_statistic_item = class + Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { + action x = class + _result { + _vislevel = 3; + + width = Expression "Width" 5; + height = Expression "Height" 5; + statisticType = Magick.StatType_widget; + + command = Magick.command [ + "\"%s\"", + "-statistic", + statisticType._flag, + print width.expr ++ "x" ++ print height.expr, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_swirl_item = class + Menuaction "_Swirl" "swirl around the centre" { + action x = class + _result { + _vislevel = 3; + + angle = Magick.angle_widget; + + command = Magick.command [ + "\"%s\"", + "-swirl", + print angle.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_thresholdMenu_item = class + Menupullright "_Threshold" "make black or white" { + + Magick_threshold_item = class + Menuaction "_Threshold" "apply black/white threshold" { + action x = class + _result { + _vislevel = 3; + + intensity = Magick.intensity_widget; + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + intensity._flag, + channels._flag, + "-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_blackThreshold_item = class + Menuaction "_Black threshold" "where below threshold set to black" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-black-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_whiteThreshold_item = class + Menuaction "_White threshold" "where above threshold set to white" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + threshold = Scale "Threshold (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-white-threshold", + "\"" ++ print threshold.value ++ "%%\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_latThreshold_item = class + Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { + action x = class + _result { + _vislevel = 3; + + width = Expression "Width" 10; + height = Expression "Height" 10; + offset = Scale "Offset (percent)" (-100) 100 0; + // note: "-lat" doesn't respond to channels + + command = Magick.command [ + "\"%s\"", + "-lat", + concat ["\"", print width.expr, "x", print height.expr, + (if offset.value >= 0 then "+" else ""), print offset.value, + "%%\"" + ], + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + + Magick_randThreshold_item = class + Menuaction "_Random Threshold" "between specified limits, apply random threshold" { + action x = class + _result { + _vislevel = 3; + + channels = Magick.channels_widget; + low = Scale "Low threshold" 0 100 10; + high = Scale "High threshold" 0 100 90; + isPc = Toggle "Thresholds are percent" true; + + command = Magick.command [ + "\"%s\"", + channels._flag, + "-random-threshold", + "\"" ++ print low.value ++ "x" ++ print high.value ++ + (if isPc then "%%" else "") ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } + } + +} // end ThresholdMenu_item + + +// Note: alternatives include: +// convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif + +Magick_tile_item = class + Menuaction "_Tile" "fill given size with tiled image" { + action x = class + _result { + _vislevel = 3; + + size = Magick.Size_widget; + + command = Magick.command [ + size._flag, + "tile:" ++ "\"%s\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_tint_item = class + Menuaction "_Tint" "apply a tint" { + action x = class + _result { + _vislevel = 3; + + foreground = Magick.foreground_widget; + amount = Scale "amount (percent)" 0 100 50; + + command = Magick.command [ + "\"%s\"", + foreground._flag, + "-tint", + // snibgo note: although the amount is a percentage, it doesn't need "%" character. + print amount.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_transparent_item = class + Menuaction "_Transparent" "make this colour transparent" { + action x = class + _result { + _vislevel = 3; + + changeColour = Magick.changeCol_widget; + + command = Magick.command [ + "\"%s\"", + "-fuzz", + "\"" ++ print changeColour.fuzz.value ++ "%%\"", + (if changeColour.nonMatch then "+" else "-") ++ "transparent", + "\"" ++ changeColour.colour._flag ++ "\"", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_trim_item = class + Menuaction "_Trim" "trims away border" { + action x = class + _result { + _vislevel = 3; + + fuzz = Magick.fuzz_widget; + + command = Magick.command [ + "\"%s\"", + "-fuzz", + "\"" ++ print fuzz.value ++ "%%\"", + "-trim +repage", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_uniqueCols_item = class + Menuaction "_Unique colours" "discard all but one of any pixel color" { + action x = class + _result { + _vislevel = 3; + + command = Magick.command [ + "\"%s\"", + "-unique-colors", + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_vignette_item = class + Menuaction "_Vignette" "soften the edges in vignette style" { + action x = class + _result { + _vislevel = 3; + + radius = Magick.blur_rad_widget; + sigma = Magick.sigma_widget; + rx = Scale "Rolloff x (percent)" 0 100 10; + ry = Scale "Rolloff y (percent)" 0 100 10; + background = Magick.background_widget; + + command = Magick.command [ + "\"%s\"", + background._flag, + "-vignette", + print radius.value ++ "x" ++ print sigma.value ++ + (if rx.value >= 0 then "+" else "") ++ print rx.value ++ + (if ry.value >= 0 then "+" else "") ++ print ry.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + +Magick_wave_item = class + Menuaction "_Wave" "shear the columns into a sine wave" { + action x = class + _result { + _vislevel = 3; + + amplitude = Scale "Amplitude (pixels)" 0 100 10; + wavelength = Scale "Wavelength (pixels)" 0 100 10; + + command = Magick.command [ + "\"%s\"", + "-wave", + print amplitude.value ++ "x" ++ print wavelength.value, + "\"%s\"" + ]; + + _result = Magick.system command x; + } +} + + diff --git a/share/nip2/compat/8.6/Makefile.am b/share/nip2/compat/8.6/Makefile.am new file mode 100644 index 00000000..ce260fa8 --- /dev/null +++ b/share/nip2/compat/8.6/Makefile.am @@ -0,0 +1,26 @@ +startdir = $(pkgdatadir)/compat/8.6 + +start_DATA = \ + Colour.def \ + _convert.def \ + Filter.def \ + _generate.def \ + Histogram.def \ + Image.def \ + _joe_extra.def \ + _joe_utilities.def \ + _list.def \ + _magick.def \ + Magick.def \ + Math.def \ + Matrix.def \ + _Object.def \ + Object.def \ + _predicate.def \ + _stdenv.def \ + Tasks.def \ + _types.def \ + Widgets.def + +EXTRA_DIST = $(start_DATA) + diff --git a/share/nip2/compat/8.6/Math.def b/share/nip2/compat/8.6/Math.def new file mode 100644 index 00000000..34a40a66 --- /dev/null +++ b/share/nip2/compat/8.6/Math.def @@ -0,0 +1,588 @@ +Math_arithmetic_item = class + Menupullright "_Arithmetic" "basic arithmetic for objects" { + Add_item = class + Menuaction "_Add" "add a and b" { + action a b = map_binary add a b; + } + + Subtract_item = class + Menuaction "_Subtract" "subtract b from a" { + action a b = map_binary subtract a b; + } + + Multiply_item = class + Menuaction "_Multiply" "multiply a by b" { + action a b = map_binary multiply a b; + } + + Divide_item = class + Menuaction "_Divide" "divide a by b" { + action a b = map_binary divide a b; + } + + Remainder_item = class + Menuaction "_Remainder" + "remainder after integer division of a by b" { + action a b = map_binary remainder a b; + } + + sep1 = Menuseparator; + + Absolute_value_item = class + Menuaction "A_bsolute Value" "absolute value of x" { + action x = map_unary abs x; + } + + Absolute_value_vector_item = class + Menuaction "Absolute Value _Vector" + "like Absolute Value, but treat pixels as vectors" { + action x = map_unary abs_vec x; + } + + Sign_item = class + Menuaction "S_ign" "unit vector" { + action x = map_unary sign x; + } + + Negate_item = class + Menuaction "_Negate" "multiply by -1" { + action x = map_unary unary_minus x; + } +} + +Math_trig_item = class + Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { + Sin_item = class + Menuaction "_Sine" "calculate sine x" { + action x = map_unary sin x; + } + + Cos_item = class + Menuaction "_Cosine" "calculate cosine x" { + action x = map_unary cos x; + } + + Tan_item = class + Menuaction "_Tangent" "calculate tangent x" { + action x = map_unary tan x; + } + + sep1 = Menuseparator; + + Asin_item = class + Menuaction "Arc S_ine" "calculate arc sine x" { + action x = map_unary asin x; + } + + Acos_item = class + Menuaction "Arc C_osine" "calculate arc cosine x" { + action x = map_unary acos x; + } + + Atan_item = class + Menuaction "Arc T_angent" "calculate arc tangent x" { + action x = map_unary atan x; + } + + sep2 = Menuseparator; + + Rad_item = class + Menuaction "_Degrees to Radians" "convert degrees to radians" { + action x = map_unary rad x; + } + + Deg_item = class + Menuaction "_Radians to Degrees" "convert radians to degrees" { + action x = map_unary deg x; + } + + sep3 = Menuseparator; + + Angle_range_item = class + Menuaction "Angle i_n Range" + "is angle within t degrees of r, mod 360" { + action t r angle + = clock (max - angle) < 2*r + { + max = clock (t + r); + + clock a + = a + 360, a < 0; + = a - 360, a >= 360; + = a; + } + } +} + +Math_log_item = class + Menupullright "_Log" "logarithms and anti-logs" { + Exponential_item = class + Menuaction "_Exponential" "calculate e ** x" { + action x = map_unary (power e) x; + } + + Log_natural_item = class + Menuaction "Natural _Log" "log base e of x" { + action x = map_unary log x; + } + + sep1 = Menuseparator; + + Exponential10_item = class + Menuaction "E_xponential base 10" "calculate 10 ** x" { + action x = map_unary (power 10) x; + } + + Log10_item = class + Menuaction "L_og Base 10" "log base 10 of x" { + action x = map_unary log10 x; + } + + sep2 = Menuseparator; + + Raise_to_power_item = class + Menuaction "_Raise to Power" "calculate x ** y" { + action x y = map_binary power x y; + } +} + +Math_complex_item = class + Menupullright "_Complex" "operations on complex numbers and images" { + Complex_extract = class + Menupullright "_Extract" "extract fields from complex" { + Real_item = class + Menuaction "_Real" + "extract real part of complex" { + action in = map_unary re in; + } + + Imaginary_item = class + Menuaction "_Imaginary" + "extract imaginary part of complex" { + action in = map_unary im in; + } + } + + Complex_build_item = class + Menuaction "_Build" "join a and b to make a complex" { + action a b = map_binary comma a b; + } + + sep1 = Menuseparator; + + Polar_item = class + Menuaction "_Polar" + "convert real and imag to amplitude and phase" { + action a = map_unary polar a; + } + + Rectangular_item = class + Menuaction "_Rectagular" + ("convert (amplitude, phase) image to rectangular " ++ + "coordinates") { + action x = map_unary rectangular x; + } + + sep2 = Menuseparator; + + Conjugate_item = class + Menuaction "_Conjugate" "invert imaginary part" { + action x = map_unary conj x; + } +} + +Math_boolean_item = class + Menupullright "_Boolean" "bitwise boolean operations for integer objects" { + And_item = class + Menuaction "_AND" "bitwise AND of a and b" { + action a b = map_binary bitwise_and a b; + } + + Or_item = class + Menuaction "_OR" "bitwise OR of a and b" { + action a b = map_binary bitwise_or a b; + } + + Eor_item = class + Menuaction "_XOR" "bitwise exclusive or of a and b" { + action a b = map_binary eor a b; + } + + Not_item = class + Menuaction "_NOT" "invert a" { + action a = map_unary not a; + } + + sep1 = Menuseparator; + + Right_shift_item = class + Menuaction "Shift _Right" "shift a right by b bits" { + action a b = map_binary right_shift a b; + } + + Left_shift_item = class + Menuaction "Shift _Left" "shift a left by b bits" { + action a b = map_binary left_shift a b; + } + + sep2 = Menuseparator; + + If_then_else_item = class + Menuaction "_If Then Else" + "b where a is non-zero, c elsewhere" { + action a b c + = map_trinary ite a b c + { + // can't use if_then_else, we need a true trinary + ite a b c = if a then b else c; + } + } + + Bandand_item = Image_band_item.Bandand_item; + + Bandor_item = Image_band_item.Bandor_item; +} + +Math_relational_item = class + Menupullright "R_elational" "comparison operations" { + Equal_item = class + Menuaction "_Equal to" "test a equal to b" { + action a b = map_binary equal a b; + } + + Not_equal_item = class + Menuaction "_Not Equal to" "test a not equal to b" { + action a b = map_binary not_equal a b; + } + + sep1 = Menuseparator; + + More_item = class + Menuaction "_More Than" "test a strictly greater than b" { + action a b = map_binary more a b; + } + + Less_item = class + Menuaction "_Less Than" "test a strictly less than b" { + action a b = map_binary less a b; + } + + sep2 = Menuseparator; + + More_equal_item = class + Menuaction "M_ore Than or Equal to" + "test a greater than or equal to b" { + action a b = map_binary more_equal a b; + } + + Less_equal_item = class + Menuaction "L_ess Than or Equal to" + "test a less than or equal to b" { + action a b = map_binary less_equal a b; + } +} + +Math_list_item = class + Menupullright "L_ist" "operations on lists" { + Head_item = class + Menuaction "_Head" "first element in list" { + action x = map_unary hd x; + } + + Tail_item = class + Menuaction "_Tail" "list without the first element" { + action x = map_unary tl x; + } + + Last_item = class + Menuaction "_Last" "last element in list" { + action x = map_unary last x; + } + + Init_item = class + Menuaction "_Init" "list without the last element" { + action x = map_unary init x; + } + + sep1 = Menuseparator; + + Reverse_item = class + Menuaction "_Reverse" "reverse order of elements in list" { + action x = map_unary reverse x; + } + + Sort_item = class + Menuaction "_Sort" "sort list into ascending order" { + action x = map_unary sort x; + } + + Make_set_item = class + Menuaction "_Make Set" "remove duplicates from list" { + action x = map_unary mkset equal x; + } + + Transpose_list_item = class + Menuaction "Tr_anspose" + "exchange rows and columns in a list of lists" { + action x = map_unary transpose x; + } + + Concat_item = class + Menuaction "_Concat" + "flatten a list of lists into a single list" { + action l = map_unary concat l; + } + + sep2 = Menuseparator; + + Length_item = class + Menuaction "L_ength" "find the length of list" { + action x = map_unary len x; + } + + Subscript_item = class + Menuaction "S_ubscript" + "return element n from list (index from zero)" { + action n x = map_binary subscript n x; + } + + Take_item = class + Menuaction "_Take" "take the first n elements of list x" { + action n x = map_binary take n x; + } + + Drop_item = class + Menuaction "_Drop" "drop the first n elements of list x" { + action n x = map_binary drop n x; + } + + sep3 = Menuseparator; + + Join_item = class + Menuaction "_Join" "join two lists end to end" { + action a b = map_binary join a b; + } + + Difference_item = class + Menuaction "_Difference" "difference of two lists" { + action a b = map_binary difference a b; + } + + Cons_item = class + Menuaction "C_ons" "put element a on the front of list x" { + action a x = map_binary cons a x; + } + + Zip_item = class + Menuaction "_Zip" "join two lists, pairwise" { + action a b = map_binary zip2 a b; + } +} + +Math_round_item = class + Menupullright "_Round" "various rounding operations" { + /* smallest integral value not less than x + */ + Ceil_item = class + Menuaction "_Ceil" "smallest integral value not less than x" { + action x = map_unary ceil x; + } + + Floor_item = class + Menuaction "_Floor" + "largest integral value not greater than x" { + action x = map_unary floor x; + } + + Rint_item = class + Menuaction "_Round to Nearest" "round to nearest integer" { + action x = map_unary rint x; + } +} + +Math_fourier_item = class + Menupullright "_Fourier" "Fourier transform" { + Forward_item = class + Menuaction "_Forward" "fourier transform of image" { + action a = map_unary (rotquad @ fwfft) a; + } + + Reverse_item = class + Menuaction "_Reverse" "inverse fourier transform of image" { + action a = map_unary (invfft @ rotquad) a; + } + + Rotate_quadrants_item = class + Menuaction "Rotate _Quadrants" "rotate quadrants" { + action a = map_unary rotquad a; + } +} + +Math_stats_item = class + Menupullright "_Statistics" "measure various statistics of objects" { + Value_item = class + Menuaction "_Value" "value of point in object" { + action a = class _result { + _vislevel = 3; + + position = Expression "Coordinate" (0, 0); + + _result = map_binary point position.expr a; + } + } + + Mean_item = class + Menuaction "_Mean" "arithmetic mean value" { + action a = map_unary mean a; + } + + Gmean_item = class + Menuaction "_Geometric Mean" "geometric mean value" { + action a = map_unary meang a; + } + + Zmean_item = class + Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { + action a = map_unary meanze a; + } + + Deviation_item = class + Menuaction "_Standard Deviation" "standard deviation of object" { + action a = map_unary deviation a; + } + + Zdeviation_item = class + Menuaction "Z_ero-excluding Standard Deviation" + "standard deviation of non-zero elements" { + action a = map_unary deviationze a; + } + + Skew_item = class + Menuaction "S_kew" "skew of image or list or vector" { + action a = map_unary skew a; + } + + Kurtosis_item = class + Menuaction "Kurtosis" "kurtosis of image or list or vector" { + action a = map_unary kurtosis a; + } + + Stats_item = class + Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { + action a = map_unary stats a; + } + + sep1 = Menuseparator; + + Max_item = class + Menuaction "M_aximum" "maximum of object" { + action a = map_unary max a; + } + + Min_item = class + Menuaction "M_inimum" "minimum of object" { + action a = map_unary min a; + } + + Maxpos_item = class + Menuaction "_Position of Maximum" "position of maximum in object" { + action a = map_unary maxpos a; + } + + Minpos_item = class + Menuaction "P_osition of Minimum" "position of minimum in object" { + action a = map_unary minpos a; + } + + Gravity_item = class + Menuaction "Centre of _Gravity" + "position of centre of gravity of histogram" { + action a = map_unary gravity a; + } + + sep2 = Menuseparator; + + Count_set_item = class + Menuaction "_Non-zeros" "number of non-zero elements in object" { + action a + = map_unary cset a + { + cset i = (mean (i != 0) * i.width * i.height) / 255; + } + } + + Count_clear_item = class + Menuaction "_Zeros" "number of zero elements in object" { + action a + = map_unary cclear a + { + cclear i = (mean (i == 0) * i.width * i.height) / 255; + } + } + + Count_edges_item = class + Menuaction "_Edges" + "count average edges across or down image" { + action x = class + _result { + _vislevel = 3; + + edge = Option "Count" [ + "Horizontal lines", + "Vertical lines" + ] 0; + + _result + = map_unary process x + { + process image = Number (edge.labels?edge) + (im_cntlines image.value edge.value); + } + } + } + + sep3 = Menuseparator; + + Linear_regression_item = class + Menuaction "_Linear Regression" "fit a line to a set of points" { + action xes yes = linreg xes yes; + } + + Weighted_linear_regression_item = class + Menuaction "_Weighted Linear Regression" + "fit a line to a set of points and deviations" { + action xes yes devs = linregw xes yes devs; + } + + Cluster_item = class + Menuaction "_Cluster" "cluster a list of numbers" { + action l = class { + _vislevel = 3; + + thresh = Expression "Threshold" 10; + + [_r, _w] = cluster thresh.expr l; + + result = _r; + weights = _w; + } + } +} + +Math_base_item = class + Menupullright "Bas_e" "convert number bases" { + Hexadecimal_item = class + Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { + action a = map_unary (print_base 16) a; + } + + Binary_item = class + Menuaction "_Binary" "convert to binary (base 2)" { + action a = map_unary (print_base 2) a; + } + + Octal_item = class + Menuaction "_Octal" "convert to octal (base 8)" { + action a = map_unary (print_base 8) a; + } +} diff --git a/share/nip2/compat/8.6/Matrix.def b/share/nip2/compat/8.6/Matrix.def new file mode 100644 index 00000000..944e8d77 --- /dev/null +++ b/share/nip2/compat/8.6/Matrix.def @@ -0,0 +1,537 @@ + +Matrix_build_item = class + Menupullright "_New" "make a new matrix of some sort" { + + Plain_item = class + Menuaction "_Plain" "make a new plain matrix widget" { + action = Matrix (identity_matrix 3); + } + + Convolution_item = class + Menuaction "_Convolution" "make a new convolution matrix widget" { + action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; + } + + Recombination_item = class + Menuaction "_Recombination" + "make a new recombination matrix widget" { + action = Matrix_rec (identity_matrix 3); + } + + Morphology_item = class + Menuaction "_Morphology" "make a new morphology matrix widget" { + action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; + } + + sep1 = Menuseparator; + + Matrix_identity_item = class + Menuaction "_Identity" "make an identity matrix" { + action = identity (identity_matrix 5); + + identity v = class + _result { + _vislevel = 3; + + s = Expression "Size" (len v); + + _result + = Matrix (identity_matrix (to_real s)), to_real s != len v; + = Matrix v; + + Matrix_vips value scale offset filename display = identity value; + } + } + + Matrix_series_item = class + Menuaction "_Series" "make a series" { + action = series (mkseries 0 1 5); + + mkseries s t e + = transpose [[to_real s, to_real s + to_real t .. to_real e]]; + + series v = class + _result { + _vislevel = 3; + + _s = v?0?0; + _t = v?1?0 - v?0?0; + _e = (last v)?0; + + s = Expression "Start value" _s; + t = Expression "Step by" _t; + e = Expression "End value" _e; + + _result + = Matrix (mkseries s t e), + to_real s != _s || to_real t != _t || to_real e != _e + = Matrix v; + + Matrix_vips value scale offset filename display = series value; + } + } + + Matrix_square_item = class + Menuaction "_Square" "make a square matrix" { + action = square (mksquare 5); + + mksquare s = replicate s (take s [1, 1 ..]); + + square v = class + _result { + _vislevel = 3; + + s = Expression "Size" (len v); + + _result + = Matrix_con (sum v) 0 v, len v == to_real s + = Matrix_con (sum m) 0 m + { + m = mksquare (to_real s); + } + + Matrix_vips value scale offset filename display = square value; + } + } + + Matrix_circular_item = class + Menuaction "_Circular" "make a circular matrix" { + action = circle (mkcircle 3); + + mkcircle r + = map2 (map2 pyth) xes yes + { + line = [-r .. r]; + xes = replicate (2 * r + 1) line; + yes = transpose xes; + pyth a b + = 1, (a**2 + b**2) ** 0.5 <= r + = 0; + } + + circle v = class + _result { + _vislevel = 3; + + r = Expression "Radius" ((len v - 1) / 2); + + _result + = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 + = Matrix_con (sum m) 0 m + { + m = mkcircle (to_real r); + } + + Matrix_vips value scale offset filename display = circle value; + } + } + + Matrix_gaussian_item = class + Menuaction "_Gaussian" "make a gaussian matrix" { + action = class + _result { + _vislevel = 3; + + s = Scale "Sigma" 0.001 10 1; + ma = Scale "Minimum amplitude" 0 1 0.2; + integer = Toggle "Integer" false; + + _result + = fn s.value ma.value + { + fn + = im_gauss_imask, integer + = im_gauss_dmask; + } + } + } + + Matrix_laplacian_item = class + Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { + action = class + _result { + _vislevel = 3; + + s = Scale "Sigma" 0.001 10 1.5; + ma = Scale "Minimum amplitude" 0 1 0.1; + integer = Toggle "Integer" false; + + _result + = fn s.value ma.value + { + fn + = im_log_imask, integer + = im_log_dmask; + } + } + } + +} + +Matrix_to_matrix_item = class + Menuaction "Con_vert to Matrix" "convert anything to a matrix" { + action x = to_matrix x; +} + +#separator + +Matrix_extract_item = class + Menupullright "_Extract" "extract rows or columns from a matrix" { + Rows_item = class + Menuaction "_Rows" "extract rows" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from row" 0; + number = Expression "Extract this many rows" 1; + + _result + = map_unary process x + { + process x + = extract_area 0 first (get_width x) number x; + } + } + } + + Columns_item = class + Menuaction "_Columns" "extract columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Extract from column" 0; + number = Expression "Extract this many columns" 1; + + _result + = map_unary process x + { + process mat + = extract_area first 0 number (get_height x) x; + } + } + } + + Area_item = class + Menuaction "_Area" "extract area" { + action x = class + _result { + _vislevel = 3; + + left = Expression "First column" 0; + top = Expression "First row" 0; + width = Expression "Number of columns" 1; + height = Expression "Number of rows" 1; + + _result + = map_unary process x + { + process mat + = extract_area left top width height x; + } + } + } + + Diagonal_item = class + Menuaction "_Diagonal" "extract diagonal" { + action x = class + _result { + _vislevel = 3; + + which = Option "Extract" [ + "Leading Diagonal", + "Trailing Diagonal" + ] 0; + + _result + = map_unary process x + { + process mat + = mat.Matrix_base (map2 extr [0..] mat.value), + which == 0 + = mat.Matrix_base (map2 extr + [mat.width - 1, mat.width - 2 .. 0] mat.value); + extr n l = [l?n]; + } + } + } +} + +Matrix_insert_item = class + Menupullright "_Insert" "insert rows or columns into a matrix" { + // make a new 8-bit uchar image of wxh with pixels set to p + // use to generate new cells + newim w h p + = image_new w h 1 + Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; + + Rows_item = class + Menuaction "_Rows" "insert rows" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Insert at row" 0; + number = Expression "Insert this many rows" 1; + item = Expression "Set new cells to" 0; + + _result + = map_unary process x + { + process x + = foldl1 join_tb (concat [top, new, bottom]) + { + top + = [extract_area 0 0 w f x], f > 0 + = []; + new = [(if is_Matrix x then to_matrix else id) + (newim w number item.expr)]; + bottom + = [extract_area 0 f w (h - f) x], f < h + = []; + + f = to_real first; + w = get_width x; + h = get_height x; + } + } + } + } + + Columns_item = class + Menuaction "_Columns" "insert columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Insert at column" 0; + number = Expression "Insert this many columns" 1; + item = Expression "Set new cells to" 0; + + _result + = map_unary process x + { + process x + = foldl1 join_lr (concat [left, new, right]) + { + left + = [extract_area 0 0 f h x], f > 0 + = []; + new = [(if is_Matrix x then to_matrix else id) + (newim number h item.expr)]; + right + = [extract_area f 0 (w - f) h x], f < w + = []; + + f = to_real first; + w = get_width x; + h = get_height x; + } + } + } + } +} + +Matrix_delete_item = class + Menupullright "_Delete" "delete rows or columns from a matrix" { + // remove number of items, starting at first + delete first number l = take (to_real first) l ++ + drop (to_real first + to_real number) l; + + Rows_item = class + Menuaction "_Rows" "delete rows" { + + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from row" 0; + number = Expression "Delete this many rows" 1; + + _result + = map_unary process x + { + process x + = foldl1 join_tb (concat [top, bottom]) + { + top + = [extract_area 0 0 w f x], f > 0 + = []; + bottom + = [extract_area 0 b w (h - b) x], b < h + = []; + + f = to_real first; + n = to_real number; + b = f + n; + w = get_width x; + h = get_height x; + } + } + } + } + + Columns_item = class + Menuaction "_Columns" "delete columns" { + action x = class + _result { + _vislevel = 3; + + first = Expression "Delete from column" 0; + number = Expression "Delete this many columns" 1; + + _result + = map_unary process x + { + process x + = foldl1 join_lr (concat [left, right]) + { + left + = [extract_area 0 0 f h x], f > 0 + = []; + right + = [extract_area r 0 (w - r) h x], r < w + = []; + + f = to_real first; + n = to_real number; + r = f + n; + w = get_width x; + h = get_height x; + } + } + } + } +} + +Matrix_join = class + Menupullright "_Join" "join two matricies" { + Left_right_item = class + Menuaction "_Left to Right" "join two matricies left-right" { + action a b = map_binary join_lr a b; + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { + action a b = map_binary join_tb a b; + } +} + +Matrix_rotate_item = class + Menupullright "_Rotate" "clockwise rotation by fixed angles" { + + rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; + + rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; + + rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; + + Matrix_rot45_item = class + Menuaction "_45 Degrees" + "45 degree rotate (square, odd-length-sides only)" { + action x = map_unary rot45 x; + } +} + +Matrix_flip_item = Image_transform_item.Flip_item; + +Matrix_sort_item = class + Menuaction "_Sort" "sort by column or row" { + action x = class + _result { + _vislevel = 3; + + o = Option (_ "Orientation") [ + _ "Sort by column", + _ "Sort by row" + ] 0; + i = Expression (_ "Sort on index") 0; + d = Option (_ "Direction") [ + _ "Ascending", + _ "Descending" + ] 0; + + _result + = map_unary sort x + { + idx = to_real i; + pred a b + = a?idx <= b?idx, d == 0 + = a?idx >= b?idx; + sort x + = (x.Matrix_base @ sortc pred) x.value, + o == 0 + = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; + } + } +} + +#separator + +Matrix_invert_item = class + Menuaction "In_vert" "calculate inverse matrix" { + action x = map_unary (converse power (-1)) x; +} + +Matrix_transpose_item = class + Menuaction "_Transpose" "swap rows and columns" { + action x = map_unary transpose x; +} + +#separator + +Matrix_plot_scatter_item = class + Menuaction "_Plot Scatter" + "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { + action x = class + _result { + _check_args = [ + [x, "x", check_Matrix] + ]; + _vislevel = 3; + + auto = Toggle "Auto Range" true; + xmin = Expression "X range minimum" 0; + xmax = Expression "X range maximum" 1; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + + _result + = Plot options ((x2b @ get_image @ to_image) x) + { + options + = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ + range; + range + = [], auto + = [$xmin => xmin.expr, $xmax => xmax.expr, + $ymin => ymin.expr, $ymax => ymax.expr]; + + // matrix to image makes a 1-band mxn image + // we need to put columns into bands + x2b im + = bandjoin (map extract_col [0 .. w - 1]) + { + w = get_width im; + h = get_height im; + b = get_bands im; + extract_col x = extract_area x 0 1 h im; + } + } + } +} + +Matrix_plot_item = Hist_plot_item; + +Matrix_buildlut_item = class + Menuaction "_Build LUT From Scatter" + "make a lookup table from a matrix of [x,y1,y2..] coordinates" { + action x = class + _result { + _check_args = [ + [x, "x", check_Matrix] + ]; + _result = buildlut x; + } +} diff --git a/share/nip2/compat/8.6/Object.def b/share/nip2/compat/8.6/Object.def new file mode 100644 index 00000000..b1585186 --- /dev/null +++ b/share/nip2/compat/8.6/Object.def @@ -0,0 +1,49 @@ +Object_duplicate_item = class + Menuaction "_Duplicate" "take a copy of an object" { + action x = map_unary copy x; +} + +#separator + +Object_list_to_group_item = class + Menuaction "_List to Group" "turn a list of objects into a group" { + action x = to_group x; +} + +Object_group_to_list_item = class + Menuaction "_Group to List" "turn a group into a list of objects" { + action x = to_list x; +} + +#separator + +Object_break_item = class + Menuaction "_Break Up Object" + "break an object into a list of components" { + action x + = map_unary break x + { + break x + = bandsplit x, is_Image x + = map Vector x.value, is_Matrix x + = x.value, is_Vector x || is_Real x + = error "Breakup: not Image/Matrix/Vector/Real"; + } +} + +Object_assemble_item = class + Menuaction "_Assemble Objects" + "assemble a list of objects into a single object" { + action x + = map_unary ass x + { + ass x + = [], x == [] + = Vector x, is_real_list x + = Matrix x, is_matrix x + = bandjoin x, is_listof is_Image x + = Vector (map get_value x), is_listof is_Real x + = Matrix (map get_value x), is_listof is_Vector x + = error "Assemble: not list of Image/Vector/Real/image/real"; + } +} diff --git a/share/nip2/compat/8.6/Preferences.ws b/share/nip2/compat/8.6/Preferences.ws new file mode 100644 index 00000000..522c8714 --- /dev/null +++ b/share/nip2/compat/8.6/Preferences.ws @@ -0,0 +1,919 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/nip2/compat/8.6/Tasks.def b/share/nip2/compat/8.6/Tasks.def new file mode 100644 index 00000000..d0c6d711 --- /dev/null +++ b/share/nip2/compat/8.6/Tasks.def @@ -0,0 +1,952 @@ +Tasks_capture_item = class + Menupullright "_Capture" + "useful stuff for capturing and preprocessing images" { + + Csv_import_item = class + Menuaction "_CSV Import" "read a file of comma-separated values" { + action = class + _result { + _vislevel = 3; + + path = Pathname "File to load" "empty"; + start_line = Expression "Start at line" 1; + rows = Expression "Lines to read (-1 for whole file)" (-1); + whitespace = String "Whitespace characters" " \""; + separator = String "Separator characters" ",;\t"; + + _result + = Image blank, path.value == "empty" + = Image (im_csv2vips filename) + { + filename = search (expand path.value) ++ ":" ++ + "skip:" ++ print (start_line.expr - 1) ++ "," ++ + "whi:" ++ escape whitespace.value ++ "," ++ + "sep:" ++ escape separator.value ++ "," ++ + "line:" ++ print rows.expr; + + // prefix any ',' with a '\' in the separators line + escape x + = foldr prefix [] x + { + prefix x l + = '\\' : x : l, x == ',' + = x : l; + } + + blank = image_new 1 1 1 + Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W + 0 0 0; + } + } + } + + Raw_import_item = class + Menuaction "_Raw Import" "read a file of binary values" { + action = class + _result { + _vislevel = 3; + + path = Pathname "File to load" "empty"; + across = Expression "Pixels across" 100; + down = Expression "Pixels down" 100; + bytes = Expression "Bytes per pixel" 3; + skip = Expression "Skip over initial bytes" 0; + + _result + = Image blank, path.value == "empty" + = Image (im_binfile path.value + across.expr down.expr bytes.expr skip.expr) + { + blank = image_new 1 1 1 + Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W + 0 0 0; + } + } + } + + // interpret Analyze header for layout and calibration + Analyze7_header_item = class + Menuaction "_Interpret Analyze 7 Header" + "examine the Analyze header and set layout and value" { + action x + = x''' + { + // read bits of header + dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); + dim0 = dim 0 x; + dim1 = dim 1 x; + dim2 = dim 2 x; + dim3 = dim 3 x; + dim4 = dim 4 x; + dim5 = dim 5 x; + dim6 = dim 6 x; + dim7 = dim 7 x; + glmax = get_header "dsr-image_dimension.glmax" x; + cal_max = get_header "dsr-image_dimension.cal_max" x; + + // oops, now a nop + x' = x; + + // lay out higher dimensions width-ways + x'' + = grid dim2 dim3 1 x', dim0 == 3 + = grid dim2 dim3 dim4 x', dim0 == 4 + = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 + = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 + = grid (dim2 * dim4 * dim6) dim7 1 + (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', + dim0 == 7 + = error (_ "unsupported dimension " ++ dim0); + + // multiply by scale factor to get kBeq + x''' = x'' * (cal_max / glmax); + } + } + + Video_item = class + Menuaction "Capture _Video Frame" "capture a frame of still video" { + // shortcut to prefs + prefs = Workspaces.Preferences; + + action = class + _result { + _vislevel = 3; + + device = prefs.VIDEO_DEVICE; + channel = Option "Input channel" [ + "TV", + "Composite 1", + "Composite 2", + "Composite 3" + ] prefs.VIDEO_CHANNEL; + b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; + col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; + con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; + hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; + frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; + mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; + crop = Toggle "Crop image" prefs.VIDEO_CROP; + + // grab, but hide it ... if we let the crop edit + _raw_grab = Image (im_video_v4l1 device channel.value + b.value col.value con.value + hue.value frames.value); + + edit_crop + = Region _raw_grab left top width height + { + left = prefs.VIDEO_CROP_LEFT; + top = prefs.VIDEO_CROP_TOP; + width = min_pair + prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); + height = min_pair + prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); + } + + aspect_ratio = Expression "Stretch vertically by" + prefs.VIDEO_ASPECT; + + _result + = frame' + { + frame + = edit_crop, crop + = _raw_grab; + + frame' + = colour_transform_to Image_type.B_W frame, mono + = frame; + } + } + } + + Smooth_image_item = class + Menuaction "_Smooth" "remove small features from image" { + action in = class + _result { + _vislevel = 3; + + feature = Scale "Minimum feature size" 1 50 20; + + _result = map_unary (smooth feature.value) in; + } + } + + Light_correct_item = class + Menuaction "_Flatfield" "use white image w to flatfield image i" { + action w i + = map_binary wc w i + { + wc w i + = clip2fmt i.format (w' * i) + { + fac = mean w / max w; + w' = fac * (max w / w); + } + } + } + + Image_rank_item = Filter_rank_item.Image_rank_item; + + Tilt_item = Filter_tilt_item; + + sep1 = Menuseparator; + + White_balance_item = class + Menuaction "_White Balance" + "use average of small image to set white of large image" { + action a b = class + _result { + _vislevel = 3; + + white_hint = "Set image white to:"; + white = Colour_picker "Lab" [100, 0, 0]; + + _result + = map_binary wb a b + { + wb a b + = colour_transform_to (get_type image) image_xyz' + { + area x = x.width * x.height; + larger x y = area x > area y; + [image, patch] = sortc larger [a, b]; + to_xyz = colour_transform_to Image_type.XYZ; + + // white balance in XYZ + patch_xyz = to_colour (to_xyz patch); + white_xyz = to_xyz white; + + facs = (mean patch_xyz / mean white_xyz) * + (white_xyz / patch_xyz); + + image_xyz = to_xyz image; + image_xyz' = image_xyz * facs; + } + } + } + } + + Gamma_item = Image_levels_item.Gamma_item; + + Tone_item = Image_levels_item.Tone_item; + + sep2 = Menuseparator; + + Crop_item = Image_crop_item; + + Rotate_item = Image_transform_item.Rotate_item; + + Flip_item = Image_transform_item.Flip_item; + + Resize_item = Image_transform_item.Resize_item; + + Rubber_item = Image_transform_item.Image_rubber_item; + + sep3 = Menuseparator; + + ICC_item = Colour_icc_item; + + Temp_item = Colour_temperature_item; + + Find_calib_item = class + Menuaction "Find _Colour Calibration" + "find an RGB -> XYZ transform from an image of a colour chart" { + action image = class + _result { + _check_args = [ + [image, "image", check_Image] + ]; + _vislevel = 3; + + measure = Scale (_ "Measure area (%)") 1 100 50; + + sample = measure_draw 6 4 (to_real measure) image; + + // get macbeth data file to use + macbeth = Pathname "Pick a Macbeth data file" + "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; + + mode = Option "Input LUT" [ + "Linearize from chart greyscale", + "Fit intercept from chart greyscale", + "Linear input, set brightness from chart", + "Linear input" + ] 0; + + // get max of input image + _max_value = Image_format.maxval image.format; + + // measure chart image + _camera = measure_sample 6 4 (to_real measure) image; + + // load true values + _true_Lab = Matrix_file macbeth.value; + _true_XYZ = colour_transform + Image_type.LAB Image_type.XYZ _true_Lab; + + // get Ys of greyscale + _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); + + // camera greyscale (all bands) + _camera_grey = drop 18 _camera.value; + + // normalise both to 0-1 and combine + _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; + _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; + _comb + = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 + = Matrix [0: intercepts, replicate (_camera.width + 1) 1], + mode == 1 + = Matrix [[0, 0], [1, 1]] + { + intercepts = [(linreg _true_grey_Y' cam).intercept :: + cam <- transpose _camera_grey']; + } + + // make a linearising lut ... zero on left + _linear_lut = im_invertlut _comb (_max_value + 1); + + // and display it + // plot from 0 explicitly so we see the effect of mode1 (intercept + // from greyscale) + linearising_lut = Plot [$ymin => 0] _linear_lut; + + // map an image though the lineariser + linear x + = hist_map linearising_lut.value x, mode == 0 || mode == 1 + = x; + + // map the chart measurements though the lineariser + _camera' = (to_matrix @ linear @ to_image) _camera; + + // solve for RGB -> XYZ + // normalise: the 2nd row is what makes Y, so divide by that to + // get Y in 0-1. + _pinv = (transpose _camera' * _camera') ** -1; + _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); + M = _full_M / scale; + scale = sum _full_M.value?1; + + // now turn the camera to LAB and calculate dE76 + _camera'' = (to_matrix @ + colour_transform Image_type.XYZ Image_type.LAB @ + recomb M @ + multiply scale @ + to_image) _camera'; + + _dEs = map abs_vec (_camera'' - _true_Lab).value; + avg_dE76 = mean _dEs; + _max_dE = foldr max_pair 0 _dEs; + _worst = index (equal _max_dE) _dEs; + worst_patch + = name _worst ++ " (patch " ++ + print (_worst + 1) ++ ", " ++ + print _max_dE ++ " dE)" + { + name i + = macbeth_names?i, i >= 0 && i < len macbeth_names + = "Unknown"; + } + + // normalise brightness ... in linear mode, we optionally don't + // set the brightness from the Macbeth chart + norm x + = x * scale, mode != 3 + = x; + + // convert RGB camera to Lab + _result = (Image @ + colour_transform Image_type.XYZ Image_type.LAB @ + norm @ + recomb M @ + cast_float @ + linear) image.value; + } + } + + Apply_calib_item = class + Menuaction "_Apply Colour Calibration" + "apply an RGB -> LAB transform to an image" { + action a b = class + (map_binary process a b) { + process a b + = result, is_instanceof calib_name calib && is_Image image + = error (_ "bad arguments to " ++ "Calibrate_image") + { + // the name of the calib object we need + calib_name = "Tasks_capture_item.Find_calib_item.action"; + + // get the Calibrate_chart arg first + [image, calib] = sortc (const (is_instanceof calib_name)) + [a, b]; + + result = (Image @ + colour_transform Image_type.XYZ Image_type.LAB @ + calib.norm @ + recomb calib.M @ + cast_float @ + calib.linear) image.value; + } + } + } + + sep4 = Menuseparator; + + Graph_hist_item = Hist_find_item; + + Graph_bands_item = class + Menuaction "Plot _Bands" "show image bands as a graph" { + action x = class + _result { + _vislevel = 3; + + style = Option_enum "Style" Plot_style.names "Line"; + + auto = Toggle "Auto Range" true; + ymin = Expression "Y range minimum" 0; + ymax = Expression "Y range maximum" 1; + + _result + = Plot options (to_image (bands (image x))).value + { + options + = [$style => style.value] ++ + if auto then [] else + [$ymin => ymin.expr, $ymax => ymax.expr]; + + // try to make something image-like from it + image x + = extract_area x.left x.top 1 1 x.image, is_Mark x + = get_image x, has_image x + = get_image (to_image x); + + // get as [[1],[2],[3]] + bands x + = transpose [map mean (bandsplit x)]; + } + } + } +} + +Tasks_mosaic_item = class + Menupullright "_Mosaic" "build image mosaics" { + /* Check and group a point list by image. + */ + mosaic_sort_test l + = error "mosaic: not all points", + !is_listof is_Mark l + = error "mosaic: points not on two images", + !is_list_len 2 images + = error "mosaic: images do not match in format and coding", + !all_equal (map get_format l) || !all_equal (map get_coding l) + = error "mosaic: not same number of points on each image", + !foldr1 equal (map len l') + = l' + { + // test for all elements of a list equal + all_equal l = all (map (equal (hd l)) (tl l)); + + // all the different images + images = mkset pointer_equal (map get_image l); + + // find all points defined on image + test_image image p = (get_image p) === image; + find l image = filter (test_image image) l; + + // group point list by image + l' = map (find l) images; + } + + /* Sort a point group to get right before left, and within each group to + * get above before below. + */ + mosaic_sort_lr l + = l'' + { + // sort to get upper point first + above a b = a.top < b.top; + l' = map (sortc above) l; + + // sort to get right group before left group + right a b = a?0.left > b?0.left; + l'' = sortc right l'; + } + + /* Sort a point group to get top before bottom, and within each group to + * get left before right. + */ + mosaic_sort_tb l + = l'' + { + // sort to get upper point first + left a b = a.left < b.left; + l' = map (sortc left) l; + + // sort to get right group before left group + below a b = a?0.top > b?0.top; + l'' = sortc below l'; + } + + /* Put 'em together! Group by image, sort vertically (or horizontally) with + * one of the above, transpose to get pairs matched up, and flatten again. + */ + mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; + + Mosaic_1point_item = class + Menupullright "_One Point" "join two images with a single tie point" { + check_ab_args a b = [ + [a, "a", check_Mark], + [b, "b", check_Mark] + ]; + + // shortcut to prefs + prefs = Workspaces.Preferences; + search_area = prefs.MOSAIC_WINDOW_SIZE; + object_size = prefs.MOSAIC_OBJECT_SIZE; + blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; + refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; + + lr_mos _refine a b = class + Image _result { + _check_args = check_ab_args a b; + + bw = blend_width_widget; + refine = _refine; + + _result + = im_lrmosaic a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + (object_size / 2) (search_area / 2) 0 bw.value, + refine + = im_lrmerge a'.image.value b'.image.value + (b'.left - a'.left) (b'.top - a'.top) bw.value + { + [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; + } + } + + tb_mos _refine a b = class + Image _result { + _check_args = check_ab_args a b; + + bw = blend_width_widget; + refine = _refine; + + _result + = im_tbmosaic a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + (object_size / 2) (search_area / 2) 0 bw.value, + refine + = im_tbmerge a'.image.value b'.image.value + (b'.left - a'.left) (b'.top - a'.top) bw.value + { + [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; + } + } + + Left_right_item = class + Menuaction "_Left to Right" + "join two images left-right with a single tie point" { + action a b = lr_mos refine_widget a b; + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" + "join two images top-bottom with a single tie point" { + action a b = tb_mos refine_widget a b; + } + + sep1 = Menuseparator; + + Left_right_manual_item = class + Menuaction "Manual L_eft to Right" + "join left-right, no auto-adjust of tie points" { + action a b = lr_mos false a b; + } + + Top_bottom_manual_item = class + Menuaction "Manual T_op to Bottom" + "join top-bottom, no auto-adjust of tie points" { + action a b = tb_mos false a b; + } + } + + Mosaic_2point_item = class + Menupullright "_Two Point" "join two images with two tie points" { + check_abcd_args a b c d = [ + [a, "a", check_Mark], + [b, "b", check_Mark], + [c, "c", check_Mark], + [d, "d", check_Mark] + ]; + + // shortcut to prefs + prefs = Workspaces.Preferences; + search_area = prefs.MOSAIC_WINDOW_SIZE; + object_size = prefs.MOSAIC_OBJECT_SIZE; + blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; + refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; + + Left_right_item = class + Menuaction "_Left to Right" + "join two images left-right with a pair of tie points" { + action a b c d = class + Image _result { + _check_args = check_abcd_args a b c d; + + bw = blend_width_widget; + refine = refine_widget; + + _result + = im_lrmosaic1 a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + (object_size / 2) (search_area / 2) + 0 bw.value, + refine + = im_lrmerge1 a'.image.value b'.image.value + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + bw.value + { + [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; + } + } + } + + Top_bottom_item = class + Menuaction "_Top to Bottom" + "join two images top-bottom with a pair of tie points" { + action a b c d = class + Image _result { + _check_args = check_abcd_args a b c d; + + bw = blend_width_widget; + refine = refine_widget; + + _result + = im_tbmosaic1 a'.image.value b'.image.value 0 + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + (object_size / 2) (search_area / 2) + 0 bw.value, + refine + = im_tbmerge1 a'.image.value b'.image.value + a'.left a'.top b'.left b'.top + c'.left c'.top d'.left d'.top + bw.value + { + [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; + } + } + } + } + + sep1 = Menuseparator; + + Balance_item = class + Menuaction "Mosaic _Balance" + "disassemble mosaic, scale brightness to match, reassemble" { + action x + = map_unary balance x + { + balance x + = oo_unary_function balance_op x, is_class x + = im_global_balancef x + Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, + is_image x + = error (_ "bad arguments to " ++ "balance") + { + balance_op = Operator "balance" balance + Operator_type.COMPOUND_REWRAP false; + } + } + } + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Manual_balance_item = class + Menupullright "Manual B_alance" "balance tonality of user defined areas" { + prefs = Workspaces.Preferences; + + //////////////////////////////////////////////////////////////////////////////////// + Balance_find_item = class + Menuaction "_Find Values" + "calculates values required to scale and offset balance user defined areas in a given image" + /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary + * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of + * 8-bit reference masks, where the masks are white on a black background. + */ + { + action im_in m_control m_group = class + Matrix values{ + _vislevel = 1; + + _control_im = if m_control then im_in else 0; + _control_meanmax = so_meanmax _control_im; + _group_check = is_Group m_group; + _m_list = m_group.value, _group_check + = m_group; + + process m_current mat_in = mat_out + {so_values = so_calculate _control_meanmax im_in m_current; + mat_out = join [so_values] mat_in;} + + values = (foldr process [] _m_list); + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Balance_check_item = class + Menuaction "_Check Values" + "allows calculated set of scale and offset values to be checked and adjusted if required" + /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. + * Eg. Check values required to balance the secondary structure in an X-ray image. + * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, + * where the masks are white on a black background. + */ + { + action im_in m_matrix m_group = class + Image value + { + _vislevel = 3; + + blur = Scale "Blur" 1 10 1; + _blur = (blur.value/2 + 0.5), blur.value > 1 + = 1; + + _group_check = is_Group m_group; + _m_list = m_group.value, _group_check + = m_group; + + adjust = Matrix_rec mat_a + { + no_masks = len _m_list; + mat_a = replicate no_masks [0, 0]; + } + + // Apply the user defined adjustments to the inputted matrix of scale and offset values + _adjusted = map2 fn_adjust m_matrix.value adjust.value; + fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; + + _scaled_ims = map (fn_so_apply im_in) _adjusted; + fn_so_apply im so = map_unary adj im + {adj im = im * (so?0) + (so?1);} + _im_pairs = zip2 _m_list _scaled_ims; + + // Prepare black images as starting point. //////////// + _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; + _pair_start = [(_blank + 1), _blank]; + + Build = Toggle "Build Scale and Offset Correction Images" false; + + Output = class + { + _vislevel = 1; + scale_im = _build?0; + offset_im = _build?1; + so_values = Matrix _adjusted; + + _build = [Image so_images?0, Image so_images?1], Build + = ["Scale image not built.", "Offset image not built."] + { + m_list' = transpose [_m_list]; + m_all = map2 join m_list' _adjusted; + so_images = foldr process_2 _pair_start m_all; + } + } + + value = (foldr process_1 im_in_b _im_pairs).value + {im_in_b = map_unary cast_float im_in;} + + process_1 m_current im_start + = im_out + { + bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); + blended_im = im_blend bl_mask (m_current?1).value im_start.value; + im_out = Image (clip2fmt im_start.format blended_im); + } + + // Process for building scale and offset image. + process_2 current p_start + = p_out + { + im_s = if ((current?0) > 128) then current?1 else _blank; + im_o = if ((current?0) > 128) then current?2 else _blank; + + im_s' = convsep (matrix_blur _blur) (im_s != 0); + im_o' = convsep (matrix_blur _blur) (im_o != 0); + + im_s'' = im_blend im_s'.value im_s.value p_start?0; + im_o'' = im_blend im_o'.value im_o.value p_start?1; + + p_out = [im_s'', im_o'']; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Balance_apply_item = class + Menuaction "_Apply Values" + "apply scale and offset corrections, defined as image maps, to a given image" + /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an + * X-ray image an 32-bit float scale image and a 32-bit offset image. + */ + { + action im_in scale_im offset_im = class + Image value + { + _vislevel = 1; + + xfactor = im_in.width/scale_im.width; + yfactor = im_in.height/scale_im.height; + + _scale_im = resize Kernel_linear xfactor yfactor scale_im; + _offset_im = resize Kernel_linear xfactor yfactor offset_im; + + value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); + } + } +} + + Tilt_item = Filter_tilt_item; + + sep2 = Menuseparator; + + Rebuild_item = class + Menuaction "_Rebuild" + "disassemble mosaic, substitute image files and reassemble" { + action x = class + _result { + _vislevel = 3; + + old = String "In each filename, replace" "foo"; + new = String "With" "bar"; + + _result + = map_unary remosaic x + { + remosaic image + = Image (im_remosaic image.value old.value new.value); + } + } + } + + sep3 = Menuseparator; + +Clone_area_item = class + Menuaction "_Clone Area" + "replace dark or light section of im1 with pixels from im2" + { + action im1 im2 = class + _result { + _check_args = [ + [im1, "im1", check_Image], + [im2, "im2", check_Image] + ]; + _vislevel = 3; /* Region on first +image placed in the top left hand corner, + * positioned and size relative to the height and width of im1. + */ + r1 = Region_relative im1 0.05 0.05 0.05 0.05; + /* Mark on second image placed in the top left hand corner, + * positioned relative to the height and width of im2. Used to + * define _r2, the region from which the section of image is cloned + * from. + */ + p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left +p2.top r1.width r1.height; + mask = [r1 <= Options.sc, r1 >= +Options.sc]?(Options.replace); + Options = class + { + _vislevel = 3; + pause = Toggle "Pause process" true; + /* Option toggle used to define whether the user is + * replacing a dark or a light area. + */ + replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; + + // Used to select the area to be replaced. + sc = Scale "Scale cutoff" 0.01 mx (mx / 2) + {mx = Image_format.maxval im1.format;} + //Allows replacement with scale&offset balanced gaussian noise. + balance = Toggle "Balance cloned data to match surroundings." true; + //Allows replacement with scale&offset balanced + //gaussian noise. + process = Toggle "Replace area with Gaussian noise." false; + } + _result = im1, Options.pause + = Image (im_insert im1.value patch r1.left r1.top) + { r2 = Region im2 p2.left p2.top r1.width r1.height; + ref_meanmax = so_meanmax (if mask then 0 else r1); + mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], +[255, 255, 255]]; + mask_a = map_unary (dilate mask8) mask; + mask_b = convsep (matrix_blur 2) mask_a; + patch = so_balance ref_meanmax r1 r2 mask_b +Options.process, Options.balance + = so_balance ref_meanmax r1 r2 mask_b Options.process, +Options.process + = im_blend (get_image mask_b) (get_image r2) (get_image r1); + } + } + } +} + +Tasks_frame_item = Frame_item; + +Tasks_print_item = class + Menupullright "_Print" "useful stuff for image output" { + + Rotate_item = Image_transform_item.Rotate_item; + + Flip_item = Image_transform_item.Flip_item; + + Resize_item = Image_transform_item.Resize_item; + + Tone_item = Image_levels_item.Tone_item; + + Sharpen_item = class + Menuaction "_Sharpen" + "unsharp filter tuned for typical inkjet printers" { + action x = class + _result { + _vislevel = 3; + + target_dpi = Option "Sharpen for print at" [ + "400 dpi", + "300 dpi", + "150 dpi", + "75 dpi" + ] 1; + + _result + = map_unary process x + { + process image + = sharpen params?0 params?1 + params?2 params?3 params?4 params?5 + (colour_transform_to Image_type.LABQ image) + { + // sharpen params for various dpi + // just change the size of the area we search + param_table = [ + [7, 2.5, 40, 20, 0.5, 1.5], + [5, 2.5, 40, 20, 0.5, 1.5], + [3, 2.5, 40, 20, 0.5, 1.5], + [11, 2.5, 40, 20, 0.5, 1.5] + ]; + params = param_table?target_dpi; + } + } + } + } + + sep1 = Menuseparator; + + Temp_item = Colour_temperature_item; + + ICC_item = Colour_icc_item; +} diff --git a/share/nip2/compat/8.6/Widgets.def b/share/nip2/compat/8.6/Widgets.def new file mode 100644 index 00000000..c7ade22f --- /dev/null +++ b/share/nip2/compat/8.6/Widgets.def @@ -0,0 +1,46 @@ +Widget_slider_item = class + Menuaction "_Scale" "make a new scale widget" { + icon = "nip-slider-16.png"; + action = Scale "untitled scale" 0 255 128; +} + +Widget_toggle_item = class + Menuaction "_Toggle" "make a new toggle widget" { + action = Toggle "untitled toggle" false; +} + +Widget_option_item = class + Menuaction "_Option" "make a new option widget" { + action = Option "untitled option" ["option0", "option1"] 0; +} + +Widget_string_item = class + Menuaction "St_ring" "make a new string widget" { + action = String "Enter a string" "sample text"; +} + +Widget_number_item = class + Menuaction "_Number" "make a new number widget" { + action = Number "Enter a number" 42; +} + +Widget_expression_item = class + Menuaction "_Expression" "make a new expression widget" { + action = Expression "Enter an expression" 42; +} + +Widget_pathname_item = class + Menuaction "_File Chooser" "make a new file chooser widget" { + action = Pathname "Pick a file" + "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; +} + +Widget_font_item = class + Menuaction "F_ont Chooser" "make a new font chooser widget" { + action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; +} + +Widget_clock_item = class + Menuaction "_Clock" "make a new clock widget" { + action = Clock 1 1; +} diff --git a/share/nip2/compat/8.6/_Object.def b/share/nip2/compat/8.6/_Object.def new file mode 100644 index 00000000..9562f9b5 --- /dev/null +++ b/share/nip2/compat/8.6/_Object.def @@ -0,0 +1,364 @@ +/* Lots of little arg checks. Global for convenience. + */ + +check_any = [(const true), _ "any"]; +check_bool = [is_bool, _ "boolean"]; +check_real = [is_real, _ "real"]; +check_ureal = [is_ureal, _ "unsigned real"]; +check_preal = [is_preal, _ "positive real"]; +check_list = [is_list, _ "list"]; +check_real_list = [is_real_list, _ "list of real"]; +check_string = [is_string, _ "string"]; +check_string_list = [is_string_list, _ "list of string"]; +check_int = [is_int, _ "integer"]; +check_uint = [is_uint, _ "unsigned integer"]; +check_pint = [is_pint, _ "positive integer"]; +check_matrix = [is_matrix, _ "rectangular array of real"]; +check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; +check_image = [is_image, _ "image"]; +check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; +check_instance name = [is_instanceof name, name]; +check_Image = check_instance "Image"; +check_Matrix = [is_Matrix, _ "Matrix"]; +check_colour_space = [is_colour_space, + join_sep "|" Image_type.colour_spaces.names]; +check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; +check_Guide = [is_Guide, _ "HGuide|VGuide"]; +check_Colour = check_instance (_ "Colour"); +check_Mark = check_instance (_ "Mark"); + +/* Check a set of args to a class. Two members to look at: _check_args and + * _check_all. + * + * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] + * same number of lines as there are args + * + * stuff like "arg 2 must be real" + * + * - each line in _check_all is [test, "description"] + * any number of lines + * + * stuff like "to must be greater than from" + * + * generate an error dialog with a helpful message on failure. + * + * Have as a separate function to try to keep the size of _Object down. + */ +check_args x + = error message, badargs != [] || badalls != [] + = x +{ + argcheck = x._check_args; + allcheck = x._check_all; + + // indent string + indent = " "; + + // test for a condition in a check line fails + test_fail x = ! x?0; + + // set of failed argcheck indexes + badargs = map (extract 1) + (filter test_fail (zip2 (map testarg argcheck) [0..])) + { + testarg x = x?2?0 x?0; + } + + // set of failed allcheck indexes + badalls = map (extract 1) + (filter test_fail (zip2 (map hd allcheck) [0..])); + + // the error message + message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ + argmsg ++ allmsg ++ "\n" ++ + _ "where" ++ "\n" ++ arg_types ++ extra; + + // make the failed argcheck messages ... eg. ""value" should be + // real, you passed " etc. + argmsg = concat (map fmt badargs) + { + fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ + _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ + _ "you passed" ++ ":\n" ++ + indent ++ indent ++ print argcheck?n?0 ++ "\n"; + } + + // make the failed allcheck messages ... eg "condition failed: + // x < y" ... don't make a message if any typechecks have + // failed, as we'll probably error horribly + allmsg + = [], badargs != [] + = concat (map fmt badalls) ++ + _ "you passed" ++ "\n" ++ + concat (map fmt_arg argcheck) + { + fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; + fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; + } + + // make arg type notes + arg_types = join_sep "\n" (map fmt argcheck) + { + fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; + } + + // extra bit at the bottom, if we have any conditions + extra + = [], allcheck == [] + = "\n" ++ _ "and" ++ "\n" ++ all_desc; + + // make a list of all the allcheck descriptions, with a few + // spaces in front + all_desc_list = map (join indent @ extract 1) allcheck; + + // join em up to make a set of condition notes + all_desc = join_sep "\n" all_desc_list; +} + +/* Operator overloading stuff. + */ + +Operator_type = class { + ARITHMETIC = 1; // eg. add + RELATIONAL = 2; // eg. less + COMPOUND = 3; // eg. max/mean/etc. + COMPOUND_REWRAP = 4; // eg. transpose +} + +Operator op_name fn type symmetric = class { +} + +/* Form the converse of an Operator. + */ +oo_converse op + = Operator (converse_name op.op_name) + (converse op.fn) op.type op.symmetric +{ + converse_name x + = init x, last x == last "'" + = x ++ "'"; +} + +/* Given an operator name, look up the definition. + */ +oo_binary_lookup op_name + = matches?0, matches != [] + = error (_ "unknown binary operator" ++ ": " ++ print op_name) +{ + operator_table = [ + Operator "add" add + Operator_type.ARITHMETIC true, + Operator "subtract" subtract + Operator_type.ARITHMETIC false, + Operator "remainder" remainder + Operator_type.ARITHMETIC false, + Operator "power" power + Operator_type.ARITHMETIC false, + Operator "subscript" subscript + Operator_type.ARITHMETIC false, + Operator "left_shift" left_shift + Operator_type.ARITHMETIC false, + Operator "right_shift" right_shift + Operator_type.ARITHMETIC false, + Operator "divide" divide + Operator_type.ARITHMETIC false, + Operator "join" join + Operator_type.ARITHMETIC false, + Operator "multiply" multiply + Operator_type.ARITHMETIC true, + Operator "logical_and" logical_and + Operator_type.ARITHMETIC true, + Operator "logical_or" logical_or + Operator_type.ARITHMETIC true, + Operator "bitwise_and" bitwise_and + Operator_type.ARITHMETIC true, + Operator "bitwise_or" bitwise_or + Operator_type.ARITHMETIC true, + Operator "eor" eor + Operator_type.ARITHMETIC true, + Operator "comma" comma + Operator_type.ARITHMETIC false, + Operator "if_then_else" if_then_else + Operator_type.ARITHMETIC false, + Operator "equal" equal + Operator_type.RELATIONAL true, + Operator "not_equal" not_equal + Operator_type.RELATIONAL true, + Operator "less" less + Operator_type.RELATIONAL false, + Operator "less_equal" less_equal + Operator_type.RELATIONAL false + ]; + + matches = filter test_name operator_table; + test_name x = x.op_name == op_name; +} + +/* Given an operator name, look up a function that implements that + * operator. + */ +oo_unary_lookup op_name + = matches?0, matches != [] + = error (_ "unknown unary operator" ++ ": " ++ print op_name) +{ + operator_table = [ + /* Operators. + */ + Operator "cast_signed_char" cast_signed_char + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_char" cast_unsigned_char + Operator_type.ARITHMETIC false, + Operator "cast_signed_short" cast_signed_short + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_short" cast_unsigned_short + Operator_type.ARITHMETIC false, + Operator "cast_signed_int" cast_signed_int + Operator_type.ARITHMETIC false, + Operator "cast_unsigned_int" cast_unsigned_int + Operator_type.ARITHMETIC false, + Operator "cast_float" cast_float + Operator_type.ARITHMETIC false, + Operator "cast_double" cast_double + Operator_type.ARITHMETIC false, + Operator "cast_complex" cast_complex + Operator_type.ARITHMETIC false, + Operator "cast_double_complex" cast_double_complex + Operator_type.ARITHMETIC false, + Operator "unary_minus" unary_minus + Operator_type.ARITHMETIC false, + Operator "negate" negate + Operator_type.RELATIONAL false, + Operator "complement" complement + Operator_type.ARITHMETIC false, + Operator "unary_plus" unary_plus + Operator_type.ARITHMETIC false, + + /* Built in projections. + */ + Operator "re" re Operator_type.ARITHMETIC false, + Operator "im" im Operator_type.ARITHMETIC false, + Operator "hd" hd Operator_type.ARITHMETIC false, + Operator "tl" tl Operator_type.ARITHMETIC false, + + /* Maths builtins. + */ + Operator "sin" sin Operator_type.ARITHMETIC false, + Operator "cos" cos Operator_type.ARITHMETIC false, + Operator "tan" tan Operator_type.ARITHMETIC false, + Operator "asin" asin Operator_type.ARITHMETIC false, + Operator "acos" acos Operator_type.ARITHMETIC false, + Operator "atan" atan Operator_type.ARITHMETIC false, + Operator "log" log Operator_type.ARITHMETIC false, + Operator "log10" log10 Operator_type.ARITHMETIC false, + Operator "exp" exp Operator_type.ARITHMETIC false, + Operator "exp10" exp10 Operator_type.ARITHMETIC false, + Operator "ceil" ceil Operator_type.ARITHMETIC false, + Operator "floor" floor Operator_type.ARITHMETIC false + ]; + + matches = filter test_name operator_table; + test_name x = x.op_name == op_name; +} + +/* Find the matching methods in a method table. + */ +oo_method_lookup table = map (extract 0) (filter (extract 1) table); + +/* A binary op: a is a class, b may be a class ... eg. "add" a b + + two obvious ways to find a method: + + - a.oo_binary_search "add" (+) b + - b.oo_binary_search "add'" (converse (+)) a, is_class b + + if these fail but op is a symmetric operator (eg. a + b == b + a), we can + also try reversing the args + + - a.oo_binary_search "add'" (converse (+)) b + - b.oo_binary_search "add" (+) a, is_class b + + if those fail as well, but this is ==, do pointer equals as a fallback + + */ +oo_binary_function op a b + = matches1?0, + matches1 != [] + = matches2?0, + is_class b && matches2 != [] + = matches3?0, + op.symmetric && matches3 != [] + = matches4?0, + op.symmetric && is_class b && matches4 != [] + = pointer_equal a b, + op.op_name == "equal" || op.op_name == "equal'" + = not_pointer_equal a b, + op.op_name == "not_equal" || op.op_name == "not_equal'" + = error (_ "No method found for binary operator." ++ "\n" ++ + _ "left" ++ " = " ++ print a ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "right" ++ " = " ++ print b) +{ + matches1 = oo_method_lookup (a.oo_binary_table op b); + matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); + matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); + matches4 = oo_method_lookup (b.oo_binary_table op a); +} + +/* A binary op: a is not a class, b is a class ... eg. "subtract" a b + + only one way to find a method: + + - b.oo_binary_search "subtract'" (converse (-)) a + + if this fails but op is a symmetric operator (eg. a + b == b + a), we can + try reversing the args + + - b.oo_binary_search "add" (+) a, is_class b + + if that fails as well, but this is ==, do pointer equals as a fallback + + */ +oo_binary'_function op a b + = matches1?0, matches1 != [] + = matches2?0, op.symmetric && matches2 != [] + = pointer_equal a b, + op.op_name == "equal" || op.op_name == "equal'" + = not_pointer_equal a b, + op.op_name == "not_equal" || op.op_name == "not_equal'" + = error (_ "No method found for binary operator." ++ "\n" ++ + _ "left" ++ " = " ++ print a ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "right" ++ " = " ++ print b) +{ + matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); + matches2 = oo_method_lookup (b.oo_binary_table op a); +} + +oo_unary_function op x + = matches?0, matches != [] + = error (_ "No method found for unary operator." ++ "\n" ++ + _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ + _ "argument" ++ " = " ++ print x) +{ + matches = oo_method_lookup (x.oo_unary_table op); +} + +/* Base class for nip's built-in classes ... base check function, base + * operator overload functions. + */ +_Object = class { + check = check_args this; + + // these should always be defined + _check_args = []; + _check_all = []; + + /* Operator overloading stuff. + */ + oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; + oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; + oo_unary op = oo_unary_function (oo_unary_lookup op) this; + + oo_binary_table op x = []; + oo_unary_table op = []; +} diff --git a/share/nip2/compat/8.6/_convert.def b/share/nip2/compat/8.6/_convert.def new file mode 100644 index 00000000..c63f78ae --- /dev/null +++ b/share/nip2/compat/8.6/_convert.def @@ -0,0 +1,739 @@ + +/* Try to make a Matrix ... works for Vector/Image/Real, plus image/real + */ +to_matrix x + = to_matrix x.expr, is_Expression x + = x, is_Matrix x + = oo_unary_function to_matrix_op x, is_class x + = tom x +{ + to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; + + tom x + = Matrix (itom x), is_image x + = Matrix [[x]], is_real x + = Matrix [x], is_real_list x + = Matrix x, is_matrix x + = error (_ "bad arguments to " ++ "to_matrix"); + + itom i + = (im_vips2mask ((double) i)).value, is_image i + = error (_ "not image"); +} + +/* Try to make a Vector ... works for Vector/Image/Real, plus image/real + */ +to_vector x + = to_vector x.expr, is_Expression x + = x, is_Vector x + = oo_unary_function to_vector_op x, is_class x + = tov x +{ + to_vector_op = Operator "to_vector" tov Operator_type.COMPOUND false; + + tov x + = Vector (itov x), is_image x + = Vector [x], is_real x + = Vector x, is_real_list x + = Vector x?0, is_matrix x && len x == 1 + = Vector (transpose x)?0, is_matrix x && len x?0 == 1 + = error (_ "bad arguments to " ++ "to_vector"); + + itov i + = v, is_image i + = error (_ "not image") + { + m = im_vips2mask ((double) i); + v + = m.value?0, m.height == 1 + = (transpose m.value)?0, m.width == 1 + = error (_ "image is not 1xN or Nx1"); + } +} + +/* Try to make an Image ... works for Vector/Matrix/Real, plus image/real + * Special case for Colour ... pull out the colour_space and set Type in the + * image. + */ +to_image x + = to_image x.expr, is_Expression x + = Image x.value, is_Plot x + = x, is_Image x + = Image (image_set_type + (Image_type.colour_spaces.lookup 0 1 x.colour_space) + (mtoi [x.value])), + is_Colour x + = oo_unary_function to_image_op x, is_class x + = toi x +{ + to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; + + toi x + = Image x, is_image x + = Image (mtoi [[x]]), is_real x + = Image (mtoi [x]), is_real_list x + = Image (mtoi x), is_matrix x + = error (_ "bad arguments to " ++ "to_image"); + + // [[real]] -> image + mtoi m + = im_mask2vips (Matrix m), width != 3 + = joinup (im_mask2vips (Matrix m)) + { + width = len m?0; + height = len m; + joinup i + = b1 ++ b2 ++ b3 + { + b1 = extract_area 0 0 1 height i; + b2 = extract_area 1 0 1 height i; + b3 = extract_area 2 0 1 height i; + } + } +} + +// like to_image, but we do 1x1 pixel + x, then embed it up +// always make an unwrapped image for speed ... this gets used by ifthenelse +// and stuff like that +// format can be NULL, meaning set format from x +to_image_size width height bands format x + = x, is_image x + = x.value, is_Image x + = im'' +{ + // we want x to set the target format if we don't have one, so we + // can't use image_new + im = im_black 1 1 bands + x; + im' + = clip2fmt format im, format != NULL + = im; + im'' = embed 1 0 0 width height im'; +} + +/* Try to make a Colour. + */ +to_colour x + = to_colour x.expr, is_Expression x + = x, is_Colour x + = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x + = oo_unary_function to_colour_op x, is_class x + = toc x +{ + to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; + + toc x + = Colour (colour_space (get_type x)) + (map mean (bandsplit (get_image x))), + has_image x && has_type x + = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono + = Colour "sRGB" x, is_real_list x && is_list_len 3 x + = map toc x, is_matrix x + = error (_ "bad arguments to " ++ "to_colour"); + + colour_space type + = table.get_name type, table.has_name type + = error (_ "unable to make Colour from " ++ table.get_name type ++ + _ " image") + { + table = Image_type.colour_spaces; + } +} + +/* Try to make a real. (not a Real!) + */ +to_real x + = to_real x.expr, is_Expression x + = oo_unary_function to_real_op x, is_class x + = tor x +{ + to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; + + tor x + = x, is_real x + = abs x, is_complex x + = 1, is_bool x && x + = 0, is_bool x && !x + = error (_ "bad arguments to " ++ "to_real"); +} + +to_int x = (int) (to_real x); + +/* Try to make a list ... ungroup, basically. We remove the innermost layer of + * Groups. + */ +to_list x + = x.value, is_Group x && !contains_Group x.value + = Group (map to_list x.value), is_Group x + = x; + +/* Try to make a group. The outermost list objects become Group()'d. + */ +to_group x + = Group x, is_list x + = Group (map to_group x.value), is_Group x + = x; + +/* Parse a positive integer. + */ +parse_pint l + = foldl acc 0 l +{ + acc sofar ch = sofar * 10 + parse_c ch; + + /* Turn a char digit to a number. + */ + parse_c ch + = error (_ "not a digit"), !is_digit ch + = (int) ch - (int) '0'; +} + +/* Parse an integer, with an optional sign character. + */ +parse_int l + = error (_ "badly formed number"), !is_list_len 2 parts + = sign * n +{ + parts = splitpl [member "+-", is_digit] l; + + n = parse_pint parts?1; + sign + = 1, parts?0 == [] || parts?0 == "+" + = -1; +} + +/* Parse a float. + * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? + */ +parse_float l + = err, !is_list_len 4 parts + = sign * (abs ipart + fpart) * 10 ** exp +{ + err = error (_ "badly formed number"); + + parts = splitpl [ + member "+-0123456789", + member ".0123456789", + member "eE", + member "+-0123456789" + ] l; + + ipart = parse_int parts?0; + sign + = 1, ipart >= 0 + = -1; + fpart + = 0, parts?1 == []; + = err, parts?1?0 != '.' + = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); + exp + = 0, parts?2 == [] && parts?3 == [] + = err, parts?2 == [] + = parse_int parts?3; + +} + +/* Parse a time in "hh:mm:ss" into seconds. + +We could do this in one line :) + + = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map + parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, + equal ':', is_digit] l))) [0,2,4]; + +but it's totally unreadable. + + */ +parse_time l + = error (_ "badly formed time"), !is_list_len 5 parts + = s + 60 * m + 60 * 60 * h +{ + parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; + h = parse_int parts?0; + m = parse_int parts?2; + s = parse_int parts?4; +} + +/* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by + * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix + */ +D652D50_direct = Matrix + [[ 1.13529, -0.0604663, -0.0606321 ], + [ 0.0975399, 0.935024, -0.0256156 ], + [ -0.0336428, 0.0414702, 0.994135 ]]; + +D502D65_direct = D652D50_direct ** -1; + +/* Convert normalised XYZ to bradford RGB. + */ +XYZ2RGBbrad = Matrix + [[0.8951, 0.2664, -0.1614], + [-0.7502, 1.7135, 0.0367], + [0.0389, -0.0685, 1.0296]]; + +/* Convert bradford RGB to normalised XYZ. + */ +RGBbrad2XYZ = XYZ2RGBbrad ** -1; + +D93_whitepoint = Vector [89.7400, 100, 130.7700]; +D75_whitepoint = Vector [94.9682, 100, 122.5710]; +D65_whitepoint = Vector [95.0470, 100, 108.8827]; +D55_whitepoint = Vector [95.6831, 100, 92.0871]; +D50_whitepoint = Vector [96.4250, 100, 82.4680]; +A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K +B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K +C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K +E_whitepoint = Vector [100, 100, 100]; // ill. free +D3250_whitepoint = Vector [105.6590, 100, 45.8501]; + +Whitepoints = Enum [ + $D93 => D93_whitepoint, + $D75 => D75_whitepoint, + $D65 => D65_whitepoint, + $D55 => D55_whitepoint, + $D50 => D50_whitepoint, + $A => A_whitepoint, + $B => B_whitepoint, + $C => C_whitepoint, + $E => E_whitepoint, + $D3250 => D3250_whitepoint +]; + +/* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. + */ +im_D502D65 xyz + = xyz''' +{ + xyz' = xyz / D50_whitepoint; + + rgb = recomb XYZ2RGBbrad xyz'; + + // move white in bradford RGB + rgb' = rgb / Vector [0.94, 1.02, 1.33]; + + xyz'' = recomb RGBbrad2XYZ rgb'; + + // back to D65 + xyz''' = xyz'' * D65_whitepoint; +} + +/* Convert D65 XYZ to D50 using the bradford approx. + */ +im_D652D50 xyz + = xyz''' +{ + xyz' = xyz / D65_whitepoint; + + rgb = recomb XYZ2RGBbrad xyz'; + + // move white in bradford RGB + rgb' = rgb * Vector [0.94, 1.02, 1.33]; + + xyz'' = recomb RGBbrad2XYZ rgb'; + + xyz''' = xyz'' * D50_whitepoint; +} + +/* Convert D50 XYZ to Lab. + */ +im_D50XYZ2Lab xyz + = im_XYZ2Lab_temp xyz + D50_whitepoint.value?0 + D50_whitepoint.value?1 + D50_whitepoint.value?2; +im_D50Lab2XYZ lab + = im_Lab2XYZ_temp lab + D50_whitepoint.value?0 + D50_whitepoint.value?1 + D50_whitepoint.value?2; + +/* ... and mono conversions + */ +im_sRGB2mono in + = (image_set_type Image_type.B_W @ + clip2fmt (get_header "BandFmt" in) @ + recomb (Matrix [[.3, .6, .1]])) in; +im_mono2sRGB in + = image_set_type Image_type.sRGB (in ++ in ++ in); + +im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; + +im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; + +// from the 16 bit RGB and GREY formats +im_1628 x = im_clip (x >> 8); +im_162f x = x / 256; + +im_8216 x = (im_clip2us x) << 8; +im_f216 x = im_clip2us (x * 256); + +im_RGB162GREY16 in + = (image_set_type Image_type.GREY16 @ + clip2fmt (get_header "BandFmt" in) @ + recomb (Matrix [[.3, .6, .1]])) in; +im_GREY162RGB16 in + = image_set_type Image_type.RGB16 (in ++ in ++ in); + +/* The vips8 scRGB functions. + */ + +im_sRGB2scRGB in + = out +{ + [out] = vips_call "sRGB2scRGB" [in] []; +} + +im_scRGB2sRGB in + = out +{ + [out] = vips_call "scRGB2sRGB" [in] []; +} + +im_scRGB2XYZ in + = out +{ + [out] = vips_call "scRGB2XYZ" [in] []; +} + +im_XYZ2scRGB in + = out +{ + [out] = vips_call "XYZ2scRGB" [in] []; +} + +/* apply a func to an image ... make it 1 or 3 bands, and reapply other bands + * on the way out. Except if it's LABPACK. + */ +colour_apply fn x + = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK + = x'' +{ + b = get_bands x; + c = get_coding x; + + first + = extract_bands 0 3 x, b > 3 + = extract_bands 0 1 x; + tail + = extract_bands 3 (b - 3) x, b > 3 + = extract_bands 1 (b - 1) x; + x' = fn first; + x'' = x' ++ clip2fmt (get_format x') tail; +} + +/* Any 1-ary colour op, applied to Vector/Image/Matrix or image + */ +colour_unary fn x + = oo_unary_function colour_op x, is_class x + = colour_apply fn x, is_image x + = colour_apply fn [x], is_real x + = error (_ "bad arguments to " ++ "colour_unary") +{ + // COMPOUND_REWRAP ... signal to the colour class to go to image and + // back + colour_op = Operator "colour_unary" + colour_object Operator_type.COMPOUND_REWRAP false; + + colour_object x + = colour_real_list x, is_real_list x + = map colour_real_list x, is_matrix x + = colour_apply fn x, is_image x + = error (_ "bad arguments to " ++ "colour_unary"); + + colour_real_list l + = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; +} + +/* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... + * name is op name for error messages etc. + */ +colour_binary name fn x y + = oo_binary_function colour_op x y, is_class x + = oo_binary'_function colour_op x y, is_class y + = fn x y, is_image x && is_image y + = error (_ "bad arguments to " ++ name) +{ + colour_op = Operator name + colour_object Operator_type.COMPOUND_REWRAP true; + + colour_object x y + = fn x y, is_image x && is_image y + = colour_real_list fn x y, is_real_list x && is_real_list y + = map (colour_real_list fn x) y, is_real_list x && is_matrix y + = map (colour_real_list (converse fn) y) x, + is_matrix x && is_real_list y + = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y + = error (_ "bad arguments to " ++ name); + + colour_real_list fn l1 l2 + = (to_matrix (fn i1 i2)).value?0 + { + i1 = (float) (to_image (Vector l1)).value; + i2 = (float) (to_image (Vector l2)).value; + } +} + +_colour_conversion_table = [ + /* Lines are [space-from, space-to, conversion function]. Could do + * this as a big array, but table lookup feels safer. + */ + [B_W, B_W, image_set_type B_W], + [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], + [B_W, sRGB, im_mono2sRGB @ im_clip], + [B_W, scRGB, im_sRGB2scRGB @ im_mono2sRGB @ im_clip], + [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], + [B_W, GREY16, image_set_type GREY16 @ im_8216], + [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], + [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ + im_mono2sRGB @ im_clip], + + [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], + [XYZ, XYZ, image_set_type XYZ], + [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], + [XYZ, LAB, im_XYZ2Lab @ im_clip2f], + [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], + [XYZ, UCS, im_XYZ2UCS @ im_clip2f], + [XYZ, RGB, im_XYZ2disp @ im_clip2f], + [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], + [XYZ, scRGB, im_XYZ2scRGB @ im_clip2f], + [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], + [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], + + [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], + [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], + [YXY, YXY, image_set_type YXY], + [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], + [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], + [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], + [YXY, scRGB, im_XYZ2scRGB @ im_Yxy2XYZ @ im_clip2f], + [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], + [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ + im_clip2f], + + [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], + [LAB, XYZ, im_Lab2XYZ @ im_clip2f], + [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], + [LAB, LAB, image_set_type LAB @ im_clip2f], + [LAB, LCH, im_Lab2LCh @ im_clip2f], + [LAB, UCS, im_Lab2UCS @ im_clip2f], + [LAB, RGB, im_Lab2disp @ im_clip2f], + [LAB, sRGB, im_Lab2sRGB @ im_clip2f], + [LAB, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_clip2f], + [LAB, LABQ, im_Lab2LabQ @ im_clip2f], + [LAB, LABS, im_Lab2LabS @ im_clip2f], + + [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], + [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], + [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], + [LCH, LAB, im_LCh2Lab @ im_clip2f], + [LCH, LCH, image_set_type LCH], + [LCH, UCS, im_LCh2UCS @ im_clip2f], + [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], + [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], + [LCH, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], + [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], + [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], + + [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], + [UCS, XYZ, im_UCS2XYZ @ im_clip2f], + [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], + [UCS, LAB, im_UCS2Lab @ im_clip2f], + [UCS, LCH, im_UCS2LCh @ im_clip2f], + [UCS, UCS, image_set_type UCS], + [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], + [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], + [UCS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], + [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], + [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], + + [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], + [RGB, XYZ, im_disp2XYZ @ im_clip], + [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], + [RGB, LAB, im_disp2Lab @ im_clip], + [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], + [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], + [RGB, RGB, image_set_type RGB], + [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], + [RGB, scRGB, im_XYZ2scRGB @ im_disp2XYZ @ im_clip], + [RGB, RGB16, image_set_type RGB16 @ im_8216], + [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], + [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], + [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], + + [sRGB, B_W, im_sRGB2mono], + [sRGB, XYZ, im_sRGB2XYZ @ im_clip], + [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], + [sRGB, LAB, im_sRGB2Lab @ im_clip], + [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], + [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], + [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], + [sRGB, sRGB, image_set_type sRGB], + [sRGB, scRGB, im_sRGB2scRGB @ im_clip], + [sRGB, RGB16, image_set_type RGB16 @ im_8216], + [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], + [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], + [sRGB, LABS, im_Lab2LabS @ im_sRGB2Lab @ im_clip], + + [scRGB, B_W, im_sRGB2mono @ im_scRGB2sRGB], + [scRGB, XYZ, im_scRGB2XYZ], + [scRGB, YXY, im_XYZ2Yxy @ im_scRGB2XYZ], + [scRGB, LAB, im_XYZ2Lab @ im_scRGB2XYZ], + [scRGB, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_scRGB2XYZ], + [scRGB, UCS, im_XYZ2UCS @ im_scRGB2XYZ], + [scRGB, RGB, im_XYZ2disp @ im_scRGB2XYZ], + [scRGB, sRGB, im_scRGB2sRGB], + [scRGB, scRGB, image_set_type scRGB], + [scRGB, RGB16, image_set_type RGB16 @ im_8216 @ im_scRGB2sRGB], + [scRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono @ + im_scRGB2sRGB], + [scRGB, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_scRGB2XYZ], + [scRGB, LABS, im_Lab2LabS @ im_XYZ2Lab @ im_scRGB2XYZ], + + [RGB16, B_W, im_1628 @ im_sRGB2mono], + [RGB16, RGB, image_set_type RGB @ im_1628], + [RGB16, sRGB, image_set_type sRGB @ im_1628], + [RGB16, scRGB, im_sRGB2scRGB], + [RGB16, RGB16, image_set_type RGB16], + [RGB16, GREY16, im_RGB162GREY16], + [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], + + [GREY16, B_W, image_set_type B_W @ im_1628], + [GREY16, RGB, im_mono2sRGB @ im_1628], + [GREY16, sRGB, im_mono2sRGB @ im_1628], + [GREY16, scRGB, im_sRGB2scRGB @ im_mono2sRGB], + [GREY16, RGB16, im_GREY162RGB16], + [GREY16, GREY16, image_set_type GREY16], + + [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], + [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], + [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], + [LABQ, LAB, im_LabQ2Lab], + [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], + [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], + [LABQ, RGB, im_LabQ2disp], + [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], + [LABQ, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabQ2Lab], + [LABQ, LABQ, image_set_type LABQ], + [LABQ, LABS, im_LabQ2LabS], + + [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ + im_LabS2LabQ @ im_clip2s], + [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, YXY, im_XYZ2Yxy @ + im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, LAB, im_LabS2Lab], + [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], + [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], + [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], + [LABS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], + [LABS, LABQ, im_LabS2LabQ @ im_clip2s], + [LABS, LABS, image_set_type LABS] +] +{ + /* From Image_type ... repeat here for brevity. Use same ordering as + * in Colour menu for consistency. + */ + B_W = 1; + XYZ = 12; + YXY = 23; + LAB = 13; + LCH = 19; + UCS = 18; + RGB = 17; + sRGB = 22; + scRGB = 28; + RGB16 = 25; + GREY16 = 26; + LABQ = 16; + LABS = 21; +} + +/* Transform between two colour spaces. + */ +colour_transform from to in + = colour_unary _colour_conversion_table?i?2 in, i >= 0 + = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ + _ " to " ++ Image_type.type_names.get_name to) +{ + match x = x?0 == from && x?1 == to; + i = index match _colour_conversion_table; +} + +/* Transform to a colour space, assuming the type field in the input is + * correct + */ +colour_transform_to to in = colour_transform (get_type in) to in; + +/* String for path separator on this platform. + */ +path_separator = expand "$SEP"; + +/* Form a relative pathname. + * path_relative ["home", "john"] == "home/john" + * path_relative [] == "" + */ +path_relative l = join_sep path_separator l; + +/* Form an absolute pathname. + * path_absolute ["home", "john"] == "/home/john" + * path_absolute [] == "/" + * If the first component looks like 'A:', don't add an initial separator. + */ +path_absolute l + = path_relative l, + len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' + = path_separator ++ path_relative l; + +/* Parse a pathname. + * path_parse "/home/john" == ["home", "john"] + * path_parse "home/john" == ["home", "john"] + */ +path_parse str + = split (equal path_separator?0) str; + +/* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] + */ +system_search_path + = [vipsbin] ++ + map path_parse (split (equal path_sep) (expand "$PATH")) +{ + /* On some platforms we ship vips with a few extra progs. Search + * $VIPSHOME/bin first. + */ + vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; + + path_sep + = ':', expand "$SEP" == "/" + = ';'; +} + +/* Search $PATH for the first occurence of name, or "". + */ +search_for name + = hits?0, hits != [] + = "" +{ + exe_name = name ++ expand "$EXEEXT"; + form_path p = path_absolute (p ++ [exe_name]); + paths = map form_path system_search_path; + hits = dropwhile (equal []) (map search paths); +} + +/* Search $PATH for the first occurence of name, error on failure. + */ +search_for_error name + = path, path != "" + = error (exe_name ++ " not found on your search path. " ++ + "Check you have installed the program and it is on your PATH.") +{ + exe_name = name ++ expand "$EXEEXT"; + path = search_for name; +} + diff --git a/share/nip2/compat/8.6/_generate.def b/share/nip2/compat/8.6/_generate.def new file mode 100644 index 00000000..1ce3af2a --- /dev/null +++ b/share/nip2/compat/8.6/_generate.def @@ -0,0 +1,155 @@ + +/* make an image of size x by y whose pixels are their coordinates. + */ +make_xy x y = im_make_xy (to_real x) (to_real y); + +/* make an image with the specified properties ... pixel is (eg.) + * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and + * type, generate a 3 band float image, and lab2labq it before handing it + * back. + */ +image_new w h b fmt coding type pixel xoff yoff + = embed 1 0 0 w h im'''' +{ + b' + = 3, coding == Image_coding.LABPACK + = b; + fmt' + = Image_format.FLOAT, coding == Image_coding.LABPACK + = fmt; + type' + = Image_type.LAB, coding == Image_coding.LABPACK + = type; + + im = im_black 1 1 (to_real b') + pixel; + + im' = clip2fmt fmt' im; + + im'' + = im_Lab2LabQ im', coding == Image_coding.LABPACK; + = im'; + + im''' = image_set_type type' im''; + im'''' = image_set_origin xoff yoff im'''; +} + +mkim options x y b + = Image (image_new x y b + (opt $format) (opt $coding) (opt $type) + (opt $pixel) + (opt $xoffset) (opt $yoffset)) +{ + opt = get_option options [ + $format => Image_format.UCHAR, + $coding => Image_coding.NOCODING, + $type => Image_type.sRGB, + $pixel => 0, + $xoffset => 0, + $yoffset => 0 + ]; +} + +/* generate a slice of LAB space size x size pixels for L* == l + */ +lab_slice size l + = image_set_type Image_type.LAB im +{ + L = image_new size size 1 + Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; + A1 = im_fgrey (to_real size) (to_real size); + + /* im_fgrey always makes 0-1, so these ranges can be wired in. + */ + A2 = A1 * 256 - 128; + + A4 = im_rot90 A2; + im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); +} + +/* Look at Image, try to make a Colour (failing that, a Vector) which is white + * for that image type. + */ +image_white im + = colour_transform_to type white_lab, + bands == 3 && coding == Image_coding.NOCODING && + colour_spaces.present 1 type + = white_lab, + coding == Image_coding.LABPACK + = Vector (replicate bands (max_value.lookup 1 0 format)) +{ + bands = im.bands; + type = im.type; + format = im.format; + coding = im.coding; + colour_spaces = Image_type.colour_spaces; + + // white as LAB + white_lab = Colour "Lab" [100, 0, 0]; + + // maximum value for this numeric type + max_value = Table [ + [255, Image_format.DPCOMPLEX], + [255, Image_format.DOUBLE], + [255, Image_format.COMPLEX], + [255, Image_format.FLOAT], + [2 ** 31 - 1, Image_format.INT], + [2 ** 32 - 1, Image_format.UINT], + [2 ** 15 - 1, Image_format.SHORT], + [2 ** 16 - 1, Image_format.USHORT], + [2 ** 7 - 1, Image_format.CHAR], + [2 ** 8 - 1, Image_format.UCHAR] + ]; +} + +/* Make a seperable gaussian mask. + */ +matrix_gaussian_blur radius + = im_gauss_imask_sep (radius / 3) 0.2; + +/* Make a seperable square mask. + */ +matrix_blur radius + = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] +{ + mask_sq_line = replicate (2 * radius - 1) 1; +} + +/* Make a colour from a temperature. + */ +colour_from_temp T + = error (_ "T out of range"), T < 1667 || T > 25000 + = Colour "Yxy" [50, x, y] +{ + // Kim et all approximation + // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation + x + = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 + = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + + 0.2226347 * 10 ** 3 / T + 0.240390; + + y + = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + + 2.18555832 * x - 0.20219638, T < 2222 + = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + + 2.09137015 * x - 0.16748867, T < 4000 + = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + + 3.75112997 * x - 0.37001483; +} + +temp_from_colour z + = T +{ + c = colour_transform_to Image_type.YXY (to_colour z); + x = c.value?1; + y = c.value?2; + + // McCamy's approximation, see eg. + // http://en.wikipedia.org/wiki/Color_temperature#Approximation + + xe = 0.332; + ye = 0.1858; + n = (x - xe) / (y - ye); + T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; +} + diff --git a/share/nip2/compat/8.6/_joe_extra.def b/share/nip2/compat/8.6/_joe_extra.def new file mode 100644 index 00000000..1e7ac4aa --- /dev/null +++ b/share/nip2/compat/8.6/_joe_extra.def @@ -0,0 +1,505 @@ +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Frame_item = class + Menupullright "Picture _Frame" "working with images of frames" + { + //////////////////////////////////////////////////////////////////////////////////// + Build_frame_item = class + Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" + { + //////////////////////////////////////////////////////////////////////////////////// + Frame_corner_item = class + Menuaction "_Frame Corner" + "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" + { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 0; + + _type = Image_type.colour_spaces.get_name b.type; + + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + //Scale frame image if required. + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.mount_colour.value, mount_options.apply + = 0; + + //Produce scaled and resized frame. + frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Simple_frame_item = class + Menuaction "_Simple Frame" + "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" + { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 0; + + _type = Image_type.colour_spaces.get_name b.type; + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + //Scale frame image if required. + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.mount_colour.value, mount_options.apply + = 0; + + //Produce scaled and resized frame. + frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + + //////////////////////////////////////////////////////////////////////////////////// + Complex_frame_item = class + Menuaction "_Complex Frame" + "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { + prefs = Workspaces.Preferences; + + action a b = class + _result + { + _check_args = [ + [a, "a", check_Image], + [b, "b", check_Image] + ]; + _check_all = [ + [a.coding == b.coding && a.bands == b.bands, + "a.coding == b.coding && a.bands == b.bands"] + ]; + _vislevel = 3; + + ppcm = Expression "Number of pixels per cm" 25; + + /* Given the value of ppcm, distance between the inner edge of the frame + * and the outer edge of the image. +ve values mean the frame overlaps + * the image. + */ + overlap = Expression "Size of frame overlap in cm" 0; + variables = Frame_variables 1; + + _type = Image_type.colour_spaces.get_name b.type; + + //If applied the count colour be seen for -ve values of overlap + mount_options = Mount_options _type ppcm.expr; + + _cs = variables.corner_section.value; + _es = variables.edge_section.value; + _ms = variables.middle_section.value; + _ov = ppcm.expr * overlap.expr; + _sf = variables.scale_factor.expr; + _bf = variables.blend_fraction.value; + + _a = a, _sf == 1; + = a, _sf == 0; + = Image (resize Kernel_linear _sf _sf a.value); + + _im_w = b.width; + _im_h = b.height + mount_options._los, mount_options.apply + = b.height; + _os = mount_options._los, mount_options.apply + = 0; + _cl = Vector mount_options.colour.value, mount_options.apply + = 0; + + + //Produce scaled and resized frame. + frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; + //Resize image canvas and applied mount colour as required. + _pos_im = frame_position_image b frame _os _cl; + //Wrap frame round image. + _result = if (frame == 0) then _pos_im else frame; + } + } + } +//////////////////////////////////////////////////////////////////////////////////// + Straighten_frame_item = class + Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { + action a = Perspective_item.action a; + } +}; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Select_item = class + Menupullright "_Select" + "select user defined areas of an image" { + prefs = Workspaces.Preferences; + + /* Option toggle used to define whether the user is replacing a + * dark or a light area. + */ + _control = Option "Make" [ + "Selection Brighter", + "Selection Darker", + "Selection Black", + "Selection White", + "Background Black", + "Background White", + "Mask" + ] 4; + + control_selection mask im no + = [ + if mask then im * 1.2 else im * 1, + if mask then im * 0.8 else im * 1, + if mask then 0 else im, + if mask then 255 else im, + if mask then im else 0, + if mask then im else 255, + mask + ]?no; + + Rectangle = class + Menuaction "_Rectangle" + "use an Arrow or Region x to define a rectangle" + { + action x = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + im = x.image; + mask = Image m + { + rx + = x.region_rect, is_Region x + = x; + b = image_new im.width im.height 1 0 0 1 0 0 0; + w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; + m = insert_noexpand rx.nleft rx.ntop w b; + } + } + } + } + + Elipse = class + Menuaction "_Ellipse" + "use a line/arrow x to define the center point radius and direction of an ellipse" + { + action x = class + _result { + _vislevel = 3; + + control = _control; + width = Scale "Width" 0.01 1 0.5; + + _result = control_selection mask im control + { + mask = select_ellipse x width.value; + im = x.image; + } + } + } + + Tetragon = class + Menuaction "_Tetragon" + "selects the convex area defined by four points" + { + action a b c d = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + mask = select_tetragon a b c d; + im = get_image a; + } + } + + } + + Polygon = class + Menuaction "_Polygon" + "selects a polygon from an ordered group of points" + { + action pt_list = class + _result { + _vislevel = 3; + + control = _control; + + _result = control_selection mask im control + { + mask = select_polygon pt_list; + im = get_image ((pt_list.value)?0); + } + } + } + + sep1 = Menuseparator; + + Threshold_item = class + Menuaction "Thres_hold" "simple image threshold" { + action x = class + _result { + _vislevel = 3; + + t + = Scale "Threshold" 0 mx (mx / 2) + { + mx + = Image_format.maxval x.format, is_Image x + = 255; + } + + _result = map_unary (more t.value) x; + } + } + + Threshold_percent_item = class + Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { + action x = class + _result { + _vislevel = 3; + + t = Scale "Percentage of pixels" 0 100 50; + + _result + = map_unary (more (hist_thresh (t.value / 100) x)) x; + } + } + + sep2 = Menuseparator; + + Segment_item = class + Menuaction "_Segment" "break image into disjoint regions" { + action x = class + _result { + _vislevel = 3; + + segments + = Expression "Number of disjoint regions" + (map_unary (get_header "n-segments") _result); + + _result = map_unary segment x; + } + } + + Fill_item = class + Menuaction "_Fill" "fill zero pixels with the nearest non-zero" { + action x = class + Image _result { + _vislevel = 3; + + distance = Image _distance; + + [_result, _distance] = vips_call "fill_nearest" [x.value] [ + "distance" => true + ]; + } + } + +fill_nearest x + = oo_unary_function nearest_op x, is_class x + = near x, is_image x + = error (_ "bad arguments to " ++ "fill_nearest") +{ + nearest_op = Operator "fill_nearest" + fill_nearest Operator_type.COMPOUND_REWRAP false; + + near x + = [out, distance] + { + [out, distance] = vips_call "fill_nearest" [x] [ + "distance" => true + ]; + } +} + + + + + + }; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +Perspective_match_item = class + Menuaction "_Perspective Match" + "rotate, scale and skew one image to match another" { + action x y = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + _b = find_image y; + + ap1 = Mark_relative _a 0.1 0.1; + ap2 = Mark_relative _a 0.9 0.1; + ap3 = Mark_relative _a 0.1 0.9; + ap4 = Mark_relative _a 0.9 0.9; + + bp1 = Mark_relative _b 0.1 0.1; + bp2 = Mark_relative _b 0.9 0.1; + bp3 = Mark_relative _b 0.1 0.9; + bp4 = Mark_relative _b 0.9 0.9; + + _result = map_binary process x y + { + f1 = _a.width / _b.width; + f2 = _a.height / _b.height; + + rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; + pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; + + to = [ + rl?0.left, rl?0.top, + rl?1.left, rl?1.top, + rl?2.left, rl?2.top, + rl?3.left, rl?3.top + ]; + + from = [ + pl?0.left * f1, pl?0.top * f2, + pl?1.left * f1, pl?1.top * f2, + pl?2.left * f1, pl?2.top * f2, + pl?3.left * f1, pl?3.top * f2 + ]; + + trans = perspective_transform to from; + + process a b = transform 1 0 trans b2 + { + b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) + = resize Kernel_linear f1 1 b1 + {b1 = resize Kernel_linear 1 f2 b;} + } + } + } + }; + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +Perspective_item = class + Menuaction "Pe_rspective Distort" + "rotate, scale and skew an image with respect to defined points" { + action x = class + _result { + _vislevel = 3; + + // try to find an image ... for a group, get the first item + find_image x + = x, is_Image x + = find_image x?0, is_list x + = find_image x.value, is_class x && has_value x + = error "unable to find image"; + + _a = find_image x; + + dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; + ap1 = Mark_relative _a 0.1 0.1; + ap2 = Mark_relative _a 0.9 0.1; + ap3 = Mark_relative _a 0.9 0.9; + ap4 = Mark_relative _a 0.1 0.9; + + _result = map_unary process x + { + trans = [perspective_transform to from, perspective_transform from to]?(dir.value) + { + rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; + to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, + (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; + from=[0, 0, (_a.width - 1), 0, + (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; + } + + process a = transform 1 0 trans a; + } + } + }; + diff --git a/share/nip2/compat/8.6/_joe_utilities.def b/share/nip2/compat/8.6/_joe_utilities.def new file mode 100644 index 00000000..be7931df --- /dev/null +++ b/share/nip2/compat/8.6/_joe_utilities.def @@ -0,0 +1,705 @@ +/* ******Functions included in start/_NG_utilities.def:****** + * + * so_balance ref_meanmax im1 im2 mask blur gauss * + * nonzero_mean im = no_out * + * so_meanmax im = result * + * so_calculate ref_meanmax im mask = result * + * simple_frame frame im_w im_h ov cs ms bf option = result * + * corner_frame frame im_w im_h ov cs ms bf = result * + * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * + * complex_frame frame im_w im_h ov cs es ms bf option= result * + * complex_edge ra rb t bl d = rc * + * frame_lr_min r_l r_r target bw = result * + * frame_tb_min r_t r_b target bw = result * + * frame_position_image im ref os colour= result * + * merge_array bw arr = result * + * merge_to_scale im target blend dir = result * + * select_ellipse line width = mask * + * select_tetragon p1 p2 p3 p4 = mask * + * select_polygon pt_list = mask * + * perspective_transform to from = trans'' * + * sort_pts_clockwise l = l'' * + */ + +/* Called from: +* _NG_Extra.def Clone_area_item +*/ +so_balance ref_meanmax im1 im2 mask gauss + = result + { + //ref_meanmax = so_meanmax im1; + so_values = so_calculate ref_meanmax im2 mask; + im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 + = im2'' + {im2'' = im2 * (so_values?0) + (so_values?1);} + // Option to convert replacement image to scaled gaussian noise + im2_cor = im2_cor_a, gauss == false + = clip2fmt im2_cor_a.format gauss_im + {gauss_im = + gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 +(deviation im2_cor_a);} + result = im_blend (get_image mask) (get_image +im2_cor) (get_image im1); + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the mean of the non zero pixels. + * + * Called from: + * _NG_utilities so_meanmax + */ +nonzero_mean im = no_out + { + zero_im = (im == 0); + zero_mean = mean zero_im; + no_mean = mean im; + no_out = no_mean/(1 - (zero_mean/255)); + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the max and nonzero mean of an image + * + * Called from: + * _NG_utilities so_balance + * _NG_utilities so_calculate + * _NG_Extra.def Clone_area_item + * _NG_Extra.def Balance_item.Balance_find_item + */ +so_meanmax im = result + { + mean_of_im = nonzero_mean im; + adjusted_im = im - mean_of_im; + max_of_im = max adjusted_im; + + result = [mean_of_im, max_of_im]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Calculates the scale and offset required to match a reference mean and max + * + * Called from: + * _NG_utilities so_balance + * _NG_Extra.def Balance_item.Balance_find_item + */ +so_calculate ref_meanmax im mask = result + { + im' = if mask then im else 0; + im_values = so_meanmax im'; + + mean_of_ref = ref_meanmax?0; + mean_of_im = im_values?0; + + max_of_ref = ref_meanmax?1; + max_of_im = im_values?1; + + scale = (max_of_ref)/(max_of_im); + offset = mean_of_ref - (mean_of_im * scale); + result = [ scale, offset ]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Extends or shortens the central sections of a simple frame to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Simple_frame_item + */ +simple_frame frame im_w im_h ov cs ms bf option = result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + ms'' = (1 - cs); + + //Regions + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl, option == true + = Region_relative frame cs' 0 cs cs; + r_bl = Region_relative frame 0 cs' cs cs; + r_br = fliplr r_bl, option == true + = Region_relative frame cs' cs' cs cs; + + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = Region_relative frame ms' ms'' ms cs; + r_ml = Region_relative frame 0 ms' cs ms; + r_mr = fliplr r_ml, option == true + = Region_relative frame ms'' ms' cs ms; + + result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Frame_corner_item + */ +corner_frame frame im_w im_h ov cs ms bf = result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + + //Regions + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl; + r_bl = fliptb r_tl; + r_br = fliplr r_bl; + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = fliptb r_mt; + r_ml = Region_relative frame 0 ms' cs ms;; + r_mr = fliplr r_ml; + result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Completes the frame building process for simple_frame and corner_frame. + * + * _NG_utilities simple_frame + * _NG_utilities corner_frame + */ +build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result + { + //Find pixel thickness of frames section + s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); + s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); + + w_target = im_w + (2 * (s_width - ov)); + h_target = im_h + (2 * (s_height - ov)); + + blend = bf * r_tl.width; + + cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) + = w_target; + ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) + = h_target; + + //Use regions to produce sections + top = merge_to_scale r_mt cw_target blend 0; + bottom = merge_to_scale r_mb cw_target blend 0; + left = merge_to_scale r_ml ch_target blend 1; + right = merge_to_scale r_mr ch_target blend 1; + middle = Image + (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); + + //Build sections into full frame. + row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_tl, top, r_tr]]; + row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[left, middle, right]]; + row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_bl, bottom, r_br]]; + + result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) + = merge_array blend [[row_1], [row_2], [row_3]]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Extends or shortens the central sections of a frame, preserving any central details on each + * edge, to fit round a given image. + * + * Called from: + * _NG_Extra.def Frame_item.Complex_frame_item + */ +complex_frame frame im_w im_h ov cs es ms bf option= result + { + cs' = (1 - cs); + ms' = (0.5 - (ms/2)); + es' = (0.25 - (es/2)); + + r_tl = Region_relative frame 0 0 cs cs; + r_tr = fliplr r_tl, option == true + = Region_relative frame cs' 0 cs cs; + r_bl = Region_relative frame 0 cs' cs cs; + r_br = fliplr r_bl, option == true + = Region_relative frame cs' cs' cs cs; + + r_mt = Region_relative frame ms' 0 ms cs; + r_mb = Region_relative frame ms' cs' ms cs; + r_ml = Region_relative frame 0 ms' cs ms; + r_mr = fliplr r_ml, option == true + = Region_relative frame cs' ms' cs ms; + + r_et = Region_relative frame es' 0 es cs; + r_eb = Region_relative frame es' cs' es cs; + r_el = Region_relative frame 0 es' cs es; + r_er = fliplr r_el, option == true + = Region_relative frame cs' es' cs es; + + //Find pixel thickness of frames section + s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); + s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); + + w_target = im_w + (2 * (s_width - ov)); + h_target = im_h + (2 * (s_height - ov)); + min_size = foldr1 min_pair [r_tl.width, r_tl.height, + r_mt.width, r_mt.height, + r_et.width, r_et.height]; + blend = bf * min_size; + + cw_target = w_target - (2 * r_tl.width) + (2 * blend); + ch_target = h_target - (2 * r_tl.height) + (2 * blend); + + top = complex_edge r_mt r_et cw_target blend 0; + bottom = complex_edge r_mb r_eb cw_target blend 0; + left = complex_edge r_ml r_el ch_target blend 1; + right = complex_edge r_mr r_er ch_target blend 1; + middle = Image + (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); + + //Build regions into full frame. + row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_tl, top, r_tr]]; + row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[left, middle, right]]; + row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) + = merge_array blend [[r_bl, bottom, r_br]]; + result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) + = merge_array blend [[row_1], [row_2], [row_3]]; + }; + +//////////////////////////////////////////////////////////////////////////////// +/* Function called by complex frame, used to produce section + * + * Called from: + * _NG_utilities.def complex_frame + */ +complex_edge ra rb t bl d = rc + { + e1 = ceil (ra.width - t)/2, d == 0 + = 0; + e2 = 0, d == 0 + = ceil (ra.height - t)/2; + e3 = t, d == 0 + = ra.width; + e4 = ra.height, d == 0 + = t; + + check = ra.width, d == 0; + = ra.height; + + rai = get_image ra; + + t2 = (t - ra.width + (2 * bl))/2, d == 0 + = (t - ra.height + (2 * bl))/2; + + rc = ra , t <= 0 + = Image (im_extract_area rai e1 e2 e3 e4), t <= check + = merge_array bl [[rb',ra,rb']], d == 0 + = merge_array bl [[rb'],[ra],[rb']] + {rb' = merge_to_scale rb t2 bl d;} + }; + +////////////////////////////////////////////////////////////////////////////// +/* Blends two images left/right to produce an image a specific width. + * + * _NG_utilities build_frame + * _NG_utilities complex_frame + */ +frame_lr_min r_l r_r target bw = result + { + //Calculating the new widh required for each image. + no = (target/2 + bw); + n_w = no, (r_l.width > no) + = r_l.width; + + //Removing excess from what will be the middle of the final image. + n_l = im_extract_area r_l.value 0 0 n_w r_l.height; + n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; + + //Merge the two image together with a bw*2 pixel overlap. + result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Blends two images top/bottom to produce an image a specific width. + * + * _NG_utilities build_frame + * _NG_utilities complex_frame + */ +frame_tb_min r_t r_b target bw = result + { + //Calculating the new height required for each image. + no = (target/2 + bw); + n_h = no, (r_t.height > no) + = r_t.height; + + //Removing excess from what will be the middle of the final image. + n_t = im_extract_area r_t.value 0 0 r_t.width n_h; + n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; + + //Merge the two image together with a 50 pixel overlap. + result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Resixe canvas of an image to accomodate a frame and possible mount + * + * Called from: + * _NG_Extra.def Frame_item.Frame_corner_item + * _NG_Extra.def Frame_item.Simple_frame_item + * _NG_Extra.def Frame_item.Complex_frame_item + */ +frame_position_image im ref os colour= result + { + background = image_new ref.width ref.height + im.bands im.format im.coding im.type colour 0 0; + + result = insert_noexpand xp yp im background + { + xp = (ref.width - im.width)/2; + yp = (ref.height - im.height - os)/2; + } + }; + +////////////////////////////////////////////////////////////////////////////// +/* Merges an array of images together according to blend width bw + * + * Called from: + * _NG_Utilites.def build_frame + * _NG_Utilites.def complex_frame + * _NG_Utilites.def complex_edge + */ +merge_array bw arr = result + { + merge_lr bw im1 im2 = im3 + { + bw' = get_header "Xsize" (get_image im1); + bw'' = -(bw' - bw); + im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; + } + merge_tb bw im1 im2 = im3 + { + bw' = get_header "Ysize" (get_image im1); + bw'' = -(bw' - bw); + im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; + } + + im_out = (image_set_origin 0 0 @ + foldl1 (merge_tb bw) @ + map (foldl1 (merge_lr bw))) arr; + result = Image im_out; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target + * + * Called from: + * _NG_Utilites.def build_frame + * _NG_Utilites.def complex_edge + */ +merge_to_scale im target blend dir = result + { + blend' = floor blend; + + //allow fir lr or tb process + var_a = im.width, dir == 0 + = im.height; + + var_w = im.width, dir == 1 + = target, target > blend' + = blend'; + var_h = im.height, dir == 0 + = target, target > blend' + = blend'; + + //total numner of copies of im requires, taking overlap into account. + no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); + + process im no = result + { + pr_a = get_header "Xsize" (get_image im), dir == 0 + = get_header "Ysize" (get_image im); + pr_b = -(pr_a - blend' + 1); + + im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 + = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; + no' = no - 1; + + result = im', no' < 1 + = process im' no'; + } + + im_tmp = im.value, var_a > target + = process im no_loops; + + result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects an elispe based on a line and a width + * + * Called from: + * _NG_Extra.def Select_item.Elipse + */ +select_ellipse line width = mask + { + im = Image (get_image line); + + //Make a 2 band image whose value equals its coordinates. + im_coor = Image (make_xy im.width im.height); + + //Adjust the values to center tham on (line.left, line.top) + im_cent = im_coor - Vector [line.left,line.top]; + + w = line.width; + h = line.height; + + angle = 270, w == 0 && h < 0 + = 90, w == 0 && h >= 0 + = 360 + atan (h/w), w > 0 && h < 0 + = atan (h/w), w > 0 && h >= 0 + = 180 + atan (h/w); + + a = ( (h ** 2) + (w ** 2) )**0.5; + b = a * width; + + x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); + y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); + + mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Select_item.Tetragon + * _NG_Extra.def Perspective_item + */ +select_tetragon p1 p2 p3 p4 = mask + { + //Put points in clockwise order starting at the top left. + pt_list = sort_pts_clockwise [p1, p2, p3, p4]; + + pair_list = [ + [ pt_list?0, pt_list?1 ], + [ pt_list?1, pt_list?2 ], + [ pt_list?2, pt_list?3 ], + [ pt_list?3, pt_list?0 ] ]; + + //Make xy image the same size as p1.image; + im_xy = Image (make_xy p1.image.width p1.image.height); + white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); + + mask = foldl process white pair_list; + + /* Treat each pair of point as a vector going from p1 to p2, + * then select all to right of line. This is done for each pair, + * the results are all combined to select the area defined by + * the four points. + */ + process im_in pair = im_out + { + x = (pair?0).left; + y = (pair?0).top; + x'= (pair?1).left; + y'= (pair?1).top; + + w = x' - x; + h = y' - y; + + m = 0, x == x' + = (y-y')/(x-x'); + c = 0, x == x' + = ((y*x') - (y'*x))/(x' - x); + + mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 + = im_xy?1 - (im_xy?0 * m) <= c, w < 0 + = im_xy?0 <= x, w == 0 && h > 0 + = im_xy?0 >= x; + + im_out = im_in & mask; + } + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Select_item.Polygon + */ +select_polygon pt_list = mask + { + group_check = is_Group pt_list; + pt_l = pt_list.value, group_check + = pt_list; + + im = Image (get_image (pt_l?0)); + im_xy = Image (make_xy im.width im.height); + black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); + + x = im_xy?0; + y = im_xy?1; + + pt_l' = grp_trip pt_l; + + mask = foldl process black pt_l'; + + /*Takes a group adds the first two the end and then creates a lists of + *lists [[a, b, c], [b, c, d] .... [x, a, b]] + */ + grp_trip l = l'' + { + px = take 2 l; + l' = join l px; + start = [(take 3 l')]; + rest = drop 3 l'; + + process a b = c + { + x = (last a)?1; + x'= (last a)?2; + x'' = [[x, x', b]]; + c = join a x''; + } + + l'' = foldl process start rest; + }; + + process im_in triplet = im_out + { + p1 = triplet?0; + p2 = triplet?1; + p3 = triplet?2; + + //check for change in x direction between p1-p2 and p2 -p3 + dir_1 = sign (p2.left - p1.left); + dir_2 = sign (p3.left - p2.left); + dir = dir_1 + dir_2; + + //define min x limit. + min_x = p1.left, p1.left < p2.left + = p2.left + 1, dir != 0 + = p2.left; + + //define max x limit. + max_x = p1.left, p1.left > p2.left + = p2.left - 1, dir != 0 + = p2.left; + + //equation of line defined by p1 and p2 + m = line_m p1 p2; + c = line_c p1 p2; + + //Every thing below the line + im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); + + im_out = im_in ^ im_test; + } + + line_c p1 p2 = c + {m = line_m p1 p2; + c = p1.top - (m * p1.left);}; + + line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left + = 0; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Selects a tetragon based on four points. + * + * Called from: + * _NG_Extra.def Perspective_match_item + * _NG_Extra.def Perspective_item + */ +perspective_transform to from = trans'' + { + /* + * Tramsformation matrix is calculated on the bases of the following functions: + * x' = c0x + c1y + c2xy + c3 + * y' = c4x + c5y + c6xy + c7 + * + * The functions used in vips im_transform works based on the functions: + * x = x' + b0 + b2x' + b4y' + b6x'y' + * y = y' + b1 + b3x' + b5y' + b7x'y' + * + * and is applied in the form of the matrix: + * + * [[b0, b1], + * [b2, b3], + * [b4, b5], + * [b6, b7]] + * + * Therefore our required calculated matrix will be + * + * [[ c3 , c7], + * [(c0 - 1) , c4], + * [ c1 , (c5 - 1)], + * [ c2 , c6]] + * + * to = [x1, y1, x2, y2, x3, y3, x4, y4] + * from = [x1', y1', x2', y2', x3', y3', x4', y4'] + * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] + * + */ + + to' = Matrix + [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], + [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], + [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], + [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], + [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; + + from' = Matrix (transpose [from]); + + to'' = to' ** (-1); + + trans = to'' * from'; + trans' = trans.value; + trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], + [((trans'?0)?0 - 1), (trans'?4)?0 ], + [(trans'?1)?0, ((trans'?5)?0 - 1)], + [(trans'?2)?0, (trans'?6)?0 ]]; + }; + +////////////////////////////////////////////////////////////////////////////// +/* Sort a list of points into clockwise order. + * + * Called from: + * _NG_utilities.def select_tetragon + * _NG_Extra.def Perspective_match_item + * _NG_Extra.def Perspective_item + */ +sort_pts_clockwise l = l'' + { + // sort functions: + f_top a b = a.top < b.top; + f_left a b = a.left < b.left; + f_right a b = a.left > b.left; + + l' = sortc f_top l; + l'_a = take 2 l'; + l'_b = drop 2 l'; + + l''_a = sortc f_left l'_a; + l''_b = sortc f_right l'_b; + l'' = join l''_a l''_b; + }; + +Mount_options _ctype _ppcm = class + { + _vislevel = 3; + apply = Toggle "Apply mount options" false; + ls = Expression "Lower mount section bigger by (cm)" 0; + mount_colour = Colour _ctype [0, 0, 0]; + _los = ls.expr * _ppcm; + }; + +Frame_variables comp = class + { + _vislevel = 3; + + scale_factor = Expression "scale the size of the frame by" 1; + + /* These sliders define the fraction of the frames width or height is extracted + * to produce each of the particular regions. + */ + corner_section = Scale "Corner section" 0.1 1 0.5; + edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 + = "Only required for complex frames"; + middle_section = Scale "Middle section" 0.1 1 0.2; + blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; + option = Toggle "Use mirror of left-side to make right" true; + }; + diff --git a/share/nip2/compat/8.6/_list.def b/share/nip2/compat/8.6/_list.def new file mode 100644 index 00000000..d2ef4a1f --- /dev/null +++ b/share/nip2/compat/8.6/_list.def @@ -0,0 +1,482 @@ +/* any l: or all the elements of list l together + * + * any (map (equal 0) list) == true, if any element of list is zero. + * any :: [bool] -> bool + */ +any = foldr logical_or false; + +/* all l: and all the elements of list l together + * + * all (map (==0) list) == true, if every element of list is zero. + * all :: [bool] -> bool + */ +all = foldr logical_and true; + +/* concat l: join a list of lists together + * + * concat ["abc","def"] == "abcdef". + * concat :: [[*]] -> [*] + */ +concat l = foldr join [] l; + +/* delete eq x l: delete the first x from l + * + * delete equal 'b' "abcdb" == "acdb" + * delete :: (* -> bool) -> * -> [*] -> [*] + */ +delete eq a l + = [], l == [] + = y, eq a b + = b : delete eq a y +{ + b:y = l; +} + +/* difference eq a b: delete b from a + * + * difference equal "asdf" "ad" == "sf" + * difference :: (* -> bool) -> [*] -> [*] -> [*] + */ +difference = foldl @ converse @ delete; + +/* drop n l: drop the first n elements from list l + * + * drop 3 "abcd" == "d" + * drop :: num -> [*] -> [*] + */ +drop n l + = l, n <= 0 || l == [] + = drop (n - 1) (tl l); + +/* dropwhile fn l: drop while fn is true + * + * dropwhile is_digit "1234pigs" == "pigs" + * dropwhile :: (* -> bool) -> [*] -> [*] + */ +dropwhile fn l + = [], l == [] + = dropwhile fn x, fn a + = l +{ + a:x = l; +} + +/* extract n l: extract element at index n from list l + */ +extract = converse subscript; + +/* filter fn l: return all elements of l for which predicate fn holds + * + * filter is_digit "1one2two3three" = "123" + * filter :: (* -> bool) -> [*] -> [*] + */ +filter fn l + = foldr addif [] l +{ + addif x l + = x : l, fn x; + = l; +} + +/* flatten x: flatten a list of lists of things into a simple list + * + * flatten :: [[*]] -> [*] + */ +flatten x + = foldr flat [] x, is_list x + = x +{ + flat x sofar + = foldr flat sofar x, is_list x + = x : sofar; +} + +/* foldl fn st l: fold list l from the left with function fn and start st + * + * Start from the left hand end of the list (unlike foldr, see below). + * foldl is less useful (and much slower). + * + * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) + * foldl :: (* -> ** -> *) -> * -> [**] -> * + */ +foldl fn st l + = st, l == [] + = foldl fn (fn st x) xs +{ + x:xs = l; +} + +/* foldl1 fn l: like foldl, but use the 1st element as the start value + * + * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) + * foldl1 :: (* -> * -> *) -> [*] -> * + */ +foldl1 fn l + = [], l == [] + = foldl fn x xs +{ + x:xs = l; +} + +/* foldr fn st l: fold list l from the right with function fn and start st + * + * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) + * foldr :: (* -> ** -> **) -> ** -> [*] -> ** + */ +foldr fn st l + = st, l == [] + = fn x (foldr fn st xs) +{ + x:xs = l; +} + +/* foldr1 fn l: like foldr, but use the last element as the start value + * + * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) + * foldr1 :: (* -> * -> *) -> [*] -> * + */ +foldr1 fn l + = [], l == [] + = x, xs == [] + = fn x (foldr1 fn xs) +{ + x:xs = l; +} + +/* Search a list for an element, returning its index (or -1) + * + * index (equal 12) [13,12,11] == 1 + * index :: (* -> bool) -> [*] -> real + */ +index fn list + = search list 0 +{ + search l n + = -1, l == [] + = n, fn x + = search xs (n + 1) + { + x:xs = l; + } +} + +/* init l: remove last element of list l + * + * The dual of tl. + * init [1,2,3] == [1,2] + * init :: [*] -> [*] + */ +init l + = error "init of []", l == []; + = [], tl l == []; + = x : init xs +{ + x:xs = l; +} + +/* iterate f x: repeatedly apply f to x + * + * return the infinite list [x, f x, f (f x), ..]. + * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] + * iterate :: (* -> *) -> * -> [*] + */ +iterate f x = x : iterate f (f x); + +/* join_sep sep l: join a list with a separator + * + * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" + * join_sep :: [*] -> [[*]] -> [*] + */ +join_sep sep l + = foldl1 fn l +{ + fn a b = a ++ sep ++ b; +} + +/* last l: return the last element of list l + * + * The dual of hd. last [1,2,3] == 3 + * last :: [*] -> [*] + */ +last l + = error "last of []", l == [] + = x, xs == [] + = last xs +{ + x:xs = l; +} + +/* len l: length of list l + * (see also is_list_len and friends in predicate.def) + * + * len :: [*] -> num + */ +len l + = 0, l == [] + = 1 + len (tl l); + +/* limit l: return the first element of l which is equal to its predecessor + * + * useful for checking for convergence + * limit :: [*] -> * + */ +limit l + = error "incorrect use of limit", + l == [] || tl l == [] || tl (tl l) == [] + = a, a == b + = limit (b : x) +{ + a:b:x = l; +} + +/* Turn a function of n args into a function which takes a single arg of an + * n-element list. + */ +list_1ary fn x = fn x?0; +list_2ary fn x = fn x?0 x?1; +list_3ary fn x = fn x?0 x?1 x?2; +list_4ary fn x = fn x?0 x?1 x?2 x?3; +list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; +list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; +list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; + +/* map fn l: map function fn over list l + * + * map :: (* -> **) -> [*] -> [**] + */ +map f l + = [], l == []; + = f (hd l) : map f (tl l); + +/* map2 fn l1 l2: map two lists together with fn + * + * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] + */ +map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); + +/* map3 fn l1 l2 l3: map three lists together with fn + * + * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] + */ +map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); + +/* member l x: true if x is a member of list l + * + * is_digit == member "0123456789" + * member :: [*] -> * -> bool + */ +member l x = any (map (equal x) l); + +/* merge b l r: merge two lists based on a bool list + * + * merge :: [bool] -> [*] -> [*] -> [*] + */ +merge p l r + = [], p == [] || l == [] || r == [] + = a : merge z x y, c + = b : merge z x y +{ + a:x = l; + b:y = r; + c:z = p; +} + +/* mkset eq l: remove duplicates from list l using equality function + * + * mkset :: (* -> bool) -> [*] -> [*] + */ +mkset eq l + = [], l == [] + = a : filter (not @ eq a) (mkset eq x) +{ + a:x = l; +} + +/* postfix l r: add r to the end of list l + * + * The dual of ':'. + * postfix :: [*] -> ** -> [*,**] + */ +postfix l r = l ++ [r]; + +/* repeat x: make an infinite list of xes + * + * repeat :: * -> [*] + */ +repeat x = map (const x) [1..]; + +/* replicate n x: make n copies of x in a list + * + * replicate :: num -> * -> [*] + */ +replicate n x = take n (repeat x); + +/* reverse l: reverse list l + * + * reverse :: [*] -> [*] + */ +reverse l = foldl (converse cons) [] l; + +/* scanl fn st l: apply (foldl fn r) to every initial segment of a list + * + * scanl add 0 [1,2,3] == [1,3,6] + * scanl :: (* -> ** -> *) -> * -> [**] -> [*] + */ +scanl fn st l + = st, l == [] + = st' : scanl fn st' xs +{ + x:xs = l; + st' = fn st x; +} + +/* sort l: sort list l into ascending order + * + * sort :: [*] -> [*] + */ +sort l = sortc less_equal l; + +/* sortc comp l: sort list l into order using a comparision function + * + * Uses merge sort (n log n behaviour) + * sortc :: (* -> * -> bool) -> [*] -> [*] + */ +sortc comp l + = l, n <= 1 + = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) +{ + n = len l; + n2 = (int) (n / 2); + + /* merge l1 l2: merge sorted lists l1 and l2 to make a single + * sorted list + */ + merge l1 l2 + = l2, l1 == [] + = l1, l2 == [] + = a : merge x (b : y), comp a b + = b : merge (a : x) y + { + a:x = l1; + b:y = l2; + } +} + +/* sortpl pl l: sort by a list of predicates + * + * sortpl :: (* -> bool) -> [*] -> [*] + */ +sortpl pl l + = sortc (test pl) l +{ + /* Comparision function ... put true before false, if equal move on to + * the next predicate. + */ + test pl a b + = true, pl == [] + = ta, ta != tb + = test (tl pl) a b + { + ta = pl?0 a; + tb = pl?0 b; + } +} + +/* sortr l: sort list l into descending order + * + * sortr :: [*] -> [*] + */ +sortr l = sortc more l; + +/* split fn l: break a list into sections separated by many fn + * + * split is_space " hello world " == ["hello", "world"] + * split is_space " " == [] + * split :: (* -> bool) -> [*] -> [[*]] + */ +split fn l + = [], l == [] || l' == [] + = head : split fn tail +{ + nfn = not @ fn; + + l' = dropwhile fn l; + head = takewhile nfn l'; + tail = dropwhile nfn l'; +} + +/* splits fn l: break a list into sections separated by a single fn + * + * split (equal ',') ",,1" == ["", "", "1"] + * split :: (* -> bool) -> [*] -> [[*]] + */ +splits fn l + = [], l == [] + = head : splits fn tail +{ + fn' = not @ fn; + dropif x + = [], x == [] + = tl x; + + head = takewhile fn' l; + tail = dropif (dropwhile fn' l); +} + +/* splitpl fnl l: split a list up with a list of predicates + * + * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] + * splitpl :: [* -> bool] -> [*] -> [[*]] + */ +splitpl fnl l + = l, fnl == [] + = head : splitpl (tl fnl) tail +{ + head = takewhile (hd fnl) l; + tail = dropwhile (hd fnl) l; +} + +/* split_lines n l: split a list into equal length lines + * + * split_lines 4 "1234567" == ["1234", "567"] + * splitl :: int -> [*] -> [[*]] + */ +split_lines n l + = [], l == [] + = take n l : split_lines n (drop n l); + +/* take n l: take the first n elements from list l + * take :: num -> [*] -> [*] + */ +take n l + = [], n <= 0 + = [], l == [] + = hd l : take (n-1) (tl l); + +/* takewhile fn l: take from the front of a list while predicate fn holds + * + * takewhile is_digit "123onetwothree" == "123" + * takewhile :: (* -> bool) -> [*] -> [*] + */ +takewhile fn l + = [], l == [] + = hd l : takewhile fn (tl l), fn (hd l) + = []; + +/* zip2 l1 l2: zip two lists together + * + * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] + * zip2 :: [*] -> [**] -> [[*,**]] + */ +zip2 l1 l2 + = [], l1 == [] || l2 == [] + = [hd l1, hd l2] : zip2 (tl l1) (tl l2); + +/* zip3 l1 l2 l3: zip three lists together + * + * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] + * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] + */ +zip3 l1 l2 l3 + = [], l1 == [] || l2 == [] || l3 == [] + = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); diff --git a/share/nip2/compat/8.6/_magick.def b/share/nip2/compat/8.6/_magick.def new file mode 100644 index 00000000..01c22b18 --- /dev/null +++ b/share/nip2/compat/8.6/_magick.def @@ -0,0 +1,1107 @@ +/* + + ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). + + 1-Apr-2014 + Minor corrections to Geometry_widget and Alpha. + Added loads of widgets and Menuactions. + Not fully tested. + 5-Apr-2014 + Many more menu actions. + Reorganised Magick menu. + 10-Apr-2014 + Many more menu actions. + 11-Apr-2014 jcupitt + Split to separate _magick.def + Add 0-ary and 2-ary system + Put utility funcs into a Magick class + 11-Apr-2014 snibgo + Added VirtualPixelBack for cases where background is only relevant when VP=Background + 17-Apr-2014 snibgo + Many small changes. + 2-May-2014 jcupitt + Added Magick.version + 30-June-2014 + Put single-quotes around command exe to help win + 1-July-2014 + Automatically fall back to gm if we can't find convert + 17-July-2014 + better GM support + + + Last update: 17-July-2014. + + For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. + +*/ + +/* Put these in a class to avoid filling the main namespace with IM stuff. + */ + +Magick = class { + + // first gm on path, or "" + gm_path = search_for "gm"; + + // first convert on $PATH, or "" + // we check for the convert we ship first + convert_path + = vips_convert, vips_convert != "" + = search_for "convert" + { + // the convert we ship with the vips binary on some platforms, or "" + vips_convert + = search (path_absolute convert) + { + vipshome = path_parse (expand "$VIPSHOME"); + convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; + } + } + + use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; + + // Are we in GM or IM mode? + use_gm + = true, use_gm_pref && gm_path != "" + = false, !use_gm_pref && convert_path != "" + = false, convert_path != "" + = true, gm_path != "" + = error "neither IM nor GM executable found"; + + command_path + = gm_path, use_gm + = convert_path; + + // try to get the version as eg. [6, 7, 7, 10] + // GM versions are smaller, typically [1, 3, 18] + version + = map parse_int (split (member ".-") version_string) + { + [output] = vips_call "system" + ["'" ++ command_path ++ "' -version"] [$log=>true]; + version_string + = (split (equal ' ') output)?1, use_gm + = (split (equal ' ') output)?2; + } + + // make a command-line ... args is a [str] we join with spaces + command args + = "'" ++ command_path ++ "' " ++ join_sep " " args' + { + args' + = ["convert"] ++ args, use_gm + = args; + } + + // capabilities ... different versions support different features, we + // turn features on and off based on these + + // would probably be better to test for caps somehow + has_intensity + = false, use_gm + = version?0 > 6 || version?1 > 7; + has_channel + = false, use_gm + = version?0 > 6 || version?1 > 7; + + system0 cmd = system_image0 cmd; + system cmd x = map_unary (system_image cmd) x; + system2 cmd x y = map_binary (system_image2 cmd) x y; + system3 cmd x y z = map_trinary (system_image3 cmd) x y z; + + radius_widget = Scale "Radius" 0 100 10; + sigma_widget = Scale "Sigma" 0.1 10 1; + angle_widget = Scale "Angle (degrees)" (-360) 360 0; + text_widget = String "Text to draw" "AaBbCcDdEe"; + + gamma_widget = Scale "Gamma" 0 10 1; + colors_widget = Scale "Colors" 1 10 3; + resize_widget = Scale "Resize (percent)" 0 500 100; + fuzz_widget = Scale "Fuzz (percent)" 0 100 0; + blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; + + // a colour with no enclosing quotes ... use this if we know there are + // some quotes at an outer level + print_colour_nq triple + = concat ["#", concat (map fmt triple)] + { + fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); + } + + // we need the quotes because # is the comment character in *nix + print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; + + Foreground triple = class + Colour "sRGB" triple { + + _flag = "-fill " ++ print_colour triple; + + Colour_edit space triple = this.Foreground triple; + } + foreground_widget = Foreground [0, 0, 0]; + + GeneralCol triple = class + Colour "sRGB" triple { + + _flag = print_colour_nq triple; + + Colour_edit space triple = this.GeneralCol triple; + } + generalcol_widget = GeneralCol [0, 0, 0]; + + Background triple = class + Colour "sRGB" triple { + + isNone = Toggle "None (transparent black)" false; + + _flag = "-background " ++ if isNone then "None" else print_colour triple; + + Colour_edit space triple = this.Background triple; + } + background_widget = Background [255, 255, 255]; + + Bordercol triple = class + Colour "sRGB" triple { + + _flag = "-bordercolor " ++ print_colour triple; + + Colour_edit space triple = this.Bordercol triple; + } + bordercol_widget = Bordercol [0, 0, 0]; + + Mattecol triple = class + Colour "sRGB" triple { + + _flag = "-mattecolor " ++ print_colour triple; + + Colour_edit space triple = this.Mattecol triple; + } + mattecol_widget = Mattecol [189, 189, 189]; + + // FIXME: Undercolour, like many others, can have alpha channel. + // How does user input this? With a slider? + Undercol triple = class + Colour "sRGB" triple { + + isNone = Toggle "None (transparent black)" true; + + _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); + + Colour_edit space triple = this.Undercol triple; + } + undercol_widget = Undercol [0, 0, 0]; + + changeCol_widget = class { + _vislevel = 3; + + colour = GeneralCol [0, 0, 0]; + fuzz = fuzz_widget; + nonMatch = Toggle "change non-matching colours" false; + } + + Alpha alpha = class + Option_string "Alpha" [ + "On", + "Off", + "Set", + "Opaque", + "Transparent", + "Extract", + "Copy", + "Shape", + "Remove", + "Background" + ] alpha { + + _flag = "-alpha " ++ alpha; + + Option_edit caption labels value = this.Alpha labels?value; + } + alpha_widget = Alpha "On"; + + Antialias value = class + Toggle "Antialias" value { + + _flag + = "-antialias", value + = "+antialias"; + + Toggle_edit caption value = this.Antialias value; + } + antialias_widget = Antialias true; + + Builtin builtin = class + Option_string "Builtin" [ + // See http://www.imagemagick.org/script/formats.php + "rose:", + "logo:", + "wizard:", + "granite:", + "netscape:" + ] builtin { + + _flag = builtin; + + Option_edit caption labels value = this.Builtin labels?value; + } + builtin_widget = Builtin "rose:"; + + + channels_widget = class { + // FIXME? Can we grey-out alpha when we have no alpha channel, + // show CMY(K) instead of RGB(K) etc? + // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. + ChanR valueR = class + Toggle "Red" valueR { + + _flag + = "R", valueR + = ""; + + Toggle_edit caption valueR = this.ChanR valueR; + } + channelR = ChanR true; + + ChanG valueG = class + Toggle "Green" valueG { + + _flag + = "G", valueG + = ""; + + Toggle_edit caption valueG = this.ChanG valueG; + } + channelG = ChanG true; + + ChanB valueB = class + Toggle "Blue" valueB { + + _flag + = "B", valueB + = ""; + + Toggle_edit caption valueB = this.ChanB valueB; + } + channelB = ChanB true; + + ChanK valueK = class + Toggle "Black" valueK { + + _flag + = "K", valueK + = ""; + + Toggle_edit caption valueK = this.ChanK valueK; + } + channelK = ChanK true; + + ChanA valueA = class + Toggle "Alpha" valueA { + + _flag + = "A", valueA + = ""; + + Toggle_edit caption valueA = this.ChanA valueA; + } + channelA = ChanA false; + + ChanSy valueSy = class + Toggle "Sync" valueSy { + + _flag + = ",sync", valueSy + = ""; + + Toggle_edit caption valueSy = this.ChanSy valueSy; + } + channelSy = ChanSy true; + + _rgbka = concat [channelR._flag, + channelG._flag, + channelB._flag, + channelK._flag, + channelA._flag + ]; + + _flag + = "", _rgbka == "" || !has_channel + = concat [ "-channel ", + _rgbka, + channelSy._flag + ]; + } + + ch_widget = channels_widget; + + Colorspace colsp = class + Option_string "Colorspace" [ + "CIELab", + "CMY", + "CMYK", + "Gray", + "HCL", + "HCLp", + "HSB", + "HSI", + "HSL", + "HSV", + "HWB", + "Lab", + "LCH", + "LCHab", + "LCHuv", + "LMS", + "Log", + "Luv", + "OHTA", + "Rec601Luma", + "Rec601YCbCr", + "Rec709Luma", + "Rec709YCbCr", + "RGB", + "scRGB", + "sRGB", + "Transparent", + "XYZ", + "YCbCr", + "YDbDr", + "YCC", + "YIQ", + "YPbPr", + "YUV" + ] colsp { + + _flag = colsp; + + Option_edit caption labels value = this.Colorspace labels?value; + } + colorspace_widget = Colorspace "sRGB"; + + Compose comp = class + Option_string "Compose method" [ + "Atop", + "Blend", + "Blur", + "Bumpmap", + "ChangeMask", + "Clear", + "ColorBurn", + "ColorDodge", + "Colorize", + "CopyBlack", + "CopyBlue", + "CopyCyan", + "CopyGreen", + "Copy", + "CopyMagenta", + "CopyOpacity", + "CopyRed", + "CopyYellow", + "Darken", + "DarkenIntensity", + "DivideDst", + "DivideSrc", + "Dst", + "Difference", + "Displace", + "Dissolve", + "Distort", + "DstAtop", + "DstIn", + "DstOut", + "DstOver", + "Exclusion", + "HardLight", + "Hue", + "In", + "Lighten", + "LightenIntensity", + "LinearBurn", + "LinearDodge", + "LinearLight", + "Luminize", + "Mathematics", + "MinusDst", + "MinusSrc", + "Modulate", + "ModulusAdd", + "ModulusSubtract", + "Multiply", + "None", + "Out", + "Overlay", + "Over", + "PegtopLight", + "PinLight", + "Plus", + "Replace", + "Saturate", + "Screen", + "SoftLight", + "Src", + "SrcAtop", + "SrcIn", + "SrcOut", + "SrcOver", + "VividLight", + "Xor" + ] comp { + + _flag = "-compose " ++ comp; + + Option_edit caption labels value = this.Compose labels?value; + } + compose_widget = Compose "Over"; + // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. + + // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack + + coordinate_widget = class { + _vislevel = 3; + + x = Expression "X" 0; + y = Expression "Y" 0; + + _flag = concat [print x.expr, ",", print y.expr]; + }; + + Distort distort = class + Option_string "Distort" [ + "Affine", + "AffineProjection", + "ScaleRotateTranslate", + "SRT", + "Perspective", + "PerspectiveProjection", + "BilinearForward", + "BilinearReverse", + "Polynomial", + "Arc", + "Polar", + "DePolar", + "Barrel", + "BarrelInverse", + "Shepards", + "Resize" + ] distort { + + _flag = distort; + + Option_edit caption labels value = this.Distort labels?value; + } + distort_widget = Distort "SRT"; + + Dither dither = class + Option_string "Dither" [ + "None", + "FloydSteinberg", + "Riemersma" + ] dither { + + _flag = "-dither " ++ dither; + + Option_edit caption labels value = this.Dither labels?value; + } + dither_widget = Dither "FloydSteinberg"; + + Evaluate eval = class + Option_string "Evaluate operation" [ + "Abs", + "Add", + "AddModulus", + "And", + "Cos", + "Cosine", + "Divide", + "Exp", + "Exponential", + "GaussianNoise", + "ImpulseNoise", + "LaplacianNoise", + "LeftShift", + "Log", + "Max", + "Mean", + "Median", + "Min", + "MultiplicativeNoise", + "Multiply", + "Or", + "PoissonNoise", + "Pow", + "RightShift", + "Set", + "Sin", + "Sine", + "Subtract", + "Sum", + "Threshold", + "ThresholdBlack", + "ThresholdWhite", + "UniformNoise", + "Xor" + ] eval { + + _flag = "-evaluate " ++ eval; + + Option_edit caption labels value = this.Evaluate labels?value; + } + evaluate_widget = Evaluate "Add"; + + Filter filt = class + Option_string "Filter" [ + "default", + "Bartlett", + "Blackman", + "Bohman", + "Box", + "Catrom", + "Cosine", + "Cubic", + "Gaussian", + "Hamming", + "Hann", + "Hermite", + "Jinc", + "Kaiser", + "Lagrange", + "Lanczos", + "Lanczos2", + "Lanczos2Sharp", + "LanczosRadius", + "LanczosSharp", + "Mitchell", + "Parzen", + "Point", + "Quadratic", + "Robidoux", + "RobidouxSharp", + "Sinc", + "SincFast", + "Spline", + "Triangle", + "Welch" + ] filt { + + _flag = if filt == "default" then "" else "-filter " ++ filt; + + Option_edit caption labels value = this.Filter labels?value; + } + filter_widget = Filter "default"; + + Function func = class + Option_string "Function" [ + "Polynomial", + "Sinusoid", + "Arcsin", + "Arctan" + ] func { + + _flag = func; + + Option_edit caption labels value = this.Function labels?value; + } + function_widget = Function "Polynomial"; + +// "Polynomial (a[n], a[n-1], ... a[1], a[0])", +// "Sinusoid (freq, phase, amp, bias)", +// "Arcsin (width, centre, range, bias)", +// "Arctan (slope, centre, range, bias)" + + Gravity gravity = class + Option_string "Gravity" [ + "None", + "Center", + "East", + "Forget", + "NorthEast", + "North", + "NorthWest", + "SouthEast", + "South", + "SouthWest", + "West", + "Static" + ] gravity { + + _flag = "-gravity " ++ gravity; + + Option_edit caption labels value = this.Gravity labels?value; + } + gravity_widget = Gravity "Center"; + + ImageType imagetype = class + Option_string "Image type" [ + "Bilevel", + "ColorSeparation", + "ColorSeparationAlpha", + "ColorSeparationMatte", + "Grayscale", + "GrayscaleAlpha", + "GrayscaleMatte", + "Optimize", + "Palette", + "PaletteBilevelAlpha", + "PaletteBilevelMatte", + "PaletteAlpha", + "PaletteMatte", + "TrueColorAlpha", + "TrueColorMatte", + "TrueColor" + ] imagetype { + + _flag = "-type " ++ imagetype; + + Option_edit caption labels value = this.ImageType labels?value; + } + imagetype_widget = ImageType "TrueColor"; + + Intensity intensity = class + Option_string "Intensity (gray conversion)" [ + "Average", + "Brightness", + "Lightness", + "MS", + "Rec601Luma", + "Rec601Luminance", + "Rec709Luma", + "Rec709Luminance", + "RMS" + ] intensity { + + _flag + = "-intensity " ++ intensity, has_intensity + = ""; + + Option_edit caption labels value = this.Intensity labels?value; + } + intensity_widget = Intensity "Rec709Luminance"; + + Interpolate interp = class + Option_string "Interpolate" [ + "default", + "Average", + "Average4", + "Average9", + "Average16", + "Background", + "Bilinear", + "Blend", + "Integer", + "Mesh", + "Nearest", + "NearestNeighbor", + "Spline" + ] interp { + + _flag = if interp == "default" then "" else "-interpolate " ++ interp; + + Option_edit caption labels value = this.Interpolate labels?value; + } + interpolate_widget = Interpolate "default"; + + Kernel kernel = class + Option_string "Kernel" [ + "Unity", + "Gaussian", + "DoG", + "LoG", + "Blur", + "Comet", + "Binomial", + "Laplacian", + "Sobel", + "FreiChen", + "Roberts", + "Prewitt", + "Compass", + "Kirsch", + "Diamond", + "Square", + "Rectangle", + "Disk", + "Octagon", + "Plus", + "Cross", + "Ring", + "Peaks", + "Edges", + "Corners", + "Diagonals", + "LineEnds", + "LineJunctions", + "Ridges", + "ConvexHull", + "ThinSe", + "Skeleton", + "Chebyshev", + "Manhattan", + "Octagonal", + "Euclidean" + // FIXME: custom kernel + ] kernel { + + _flag = kernel; + + Option_edit caption labels value = this.Kernel labels?value; + } + kernel_widget = Kernel "Unity"; + + ModColSp msp = class + Option_string "modulate colorspace" [ + "HCL", + "HCLp", + "HSB", + "HSI", + "HSL", + "HSV", + "HWB", + "LCH" + ] msp { + + _flag = "-set option:modulate:colorspace " ++ msp; + + Option_edit caption labels value = this.ModColSp labels?value; + } + ModColSp_widget = ModColSp "HSL"; + + MorphMeth morph = class + Option_string "Method" [ + "Correlate", + "Convolve", + "Dilate", + "Erode", + "Close", + "Open", + "DilateIntensity", + "ErodeIntensity", + "CloseIntensity", + "OpenIntensity", + "Smooth", + "EdgeOut", + "EdgeIn", + "Edge", + "TopHat", + "BottomHat", + "HitAndMiss", + "Thinning", + "Thicken", + "Distance", + "IterativeDistance" + ] morph { + + _flag = morph; + + Option_edit caption labels value = this.MorphMeth labels?value; + } + morphmeth_widget = MorphMeth "Dilate"; + + Noise noise = class + Option_string "Noise" [ + "Gaussian", + "Impulse", + "Laplacian", + "Multiplicative", + "Poisson", + "Random", + "Uniform" + ] noise { + + _flag = "+noise " ++ noise; + + Option_edit caption labels value = this.Noise labels?value; + } + noise_widget = Noise "Gaussian"; + + Pattern pattern = class + Option_string "Noise" [ + // See http://www.imagemagick.org/script/formats.php + "bricks", + "checkerboard", + "circles", + "crosshatch", + "crosshatch30", + "crosshatch45", + "gray0", + "gray5", + "gray10", + "gray15", + "gray20", + "gray25", + "gray30", + "gray35", + "gray40", + "gray45", + "gray50", + "gray55", + "gray60", + "gray65", + "gray70", + "gray75", + "gray80", + "gray85", + "gray90", + "gray95", + "gray100", + "hexagons", + "horizontal", + "horizontal2", + "horizontal3", + "horizontalsaw", + "hs_bdiagonal", + "hs_cross", + "hs_diagcross", + "hs_fdiagonal", + "hs_horizontal", + "hs_vertical", + "left30", + "left45", + "leftshingle", + "octagons", + "right30", + "right45", + "rightshingle", + "smallfishscales", + "vertical", + "vertical2", + "vertical3", + "verticalbricks", + "verticalleftshingle", + "verticalrightshingle", + "verticalsaw" + ] pattern { + + _flag = "pattern:" ++ pattern; + + Option_edit caption labels value = this.Pattern labels?value; + } + pattern_widget = Pattern "bricks"; + + ResizeType resizet = class + Option_string "Resize type" [ + "resize", + "scale", + "sample", + "adaptive-resize" + ] resizet { + + _flag = resizet; + + Option_edit caption labels value = this.ResizeType labels?value; + } + ResizeType_widget = ResizeType "resize"; + + Size_widget = class { + _vislevel = 3; + + width = Expression "Width (pixels)" 64; + height = Expression "Height (pixels)" 64; + + _flag = "-size " ++ + print width.expr ++ "x" ++ print height.expr; + + }; + + StatType statt = class + Option_string "Statistic type" [ + "Gradient", + "Maximum", + "Mean", + "Median", + "Minimum", + "Mode", + "Nonpeak", + "StandardDeviation" + ] statt { + + _flag = statt; + + Option_edit caption labels value = this.StatType labels?value; + } + StatType_widget = StatType "Mean"; + + VirtualPixel vp = class + Option_string "Virtual pixel" [ + "Background", + "Black", + "CheckerTile", + "Dither", + "Edge", + "Gray", + "HorizontalTile", + "HorizontalTileEdge", + "Mirror", + "None", + "Random", + "Tile", + "Transparent", + "VerticalTile", + "VerticalTileEdge", + "White" + ] vp { + + _flag = "-virtual-pixel " ++ vp; + + _isBackground = (vp == "Background"); + + Option_edit caption labels value = this.VirtualPixel labels?value; + } + VirtualPixel_widget = VirtualPixel "Edge"; + + VirtualPixelBack_widget = class { + virtpix = Magick.VirtualPixel_widget; + background = Magick.background_widget; + _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") + ++ virtpix._flag; + } + + Geometry_widget = class { + _vislevel = 3; + + x = Expression "X" 0; + y = Expression "Y" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + AnnotGeometry_widget = class { + _vislevel = 3; + + shearX = Expression "shear X (degrees)" 0; + shearY = Expression "shear Y (degrees)" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print shearX.expr, "x", print shearY.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + OffsetGeometry_widget = class { + _vislevel = 3; + + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag = concat [format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + WhxyGeometry_widget = class { + _vislevel = 3; + + x = Expression "Width" 0; + y = Expression "Height" 0; + hoffset = Expression "Horizontal offset" 0; + voffset = Expression "Vertical offset" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format hoffset, format voffset] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + FrameGeometry_widget = class { + _vislevel = 3; + + x = Expression "Width" 0; + y = Expression "Height" 0; + outbev = Expression "Outer bevel thickness" 0; + inbev = Expression "Inner bevel thickness" 0; + + _flag + = concat [print x.expr, "x", print y.expr, + format outbev, format inbev] + { + // print an offset ... we want '+' in front of +ve strings + format offset + = concat ["+", print offset.expr], offset.expr >= 0 + = print offset.expr; + } + }; + + Font_widget = class { + _vislevel = 3; + + family = Option_string "Family" [ + "Arial", + "ArialBlack", + "AvantGarde", + "BitstreamCharter", + "Bookman", + "CenturySchoolbook", + "ComicSansMS", + "Courier", + "CourierNew", + "DejaVuSans", + "DejaVuSansMono", + "DejaVuSerif", + "Dingbats", + "FreeMono", + "FreeSans", + "FreeSerif", + "Garuda", + "Georgia", + "Helvetica", + "HelveticaNarrow", + "Impact", + "LiberationMono", + "LiberationSans", + "LiberationSerif", + "NewCenturySchlbk", + "Palatino", + "Purisa", + "Symbol", + "Times", + "TimesNewRoman", + "Ubuntu", + "Verdana", + "Webdings" + ] "Arial"; + style = Option_string "Style" [ + "Any", "Italic", "Normal", "Oblique" + ] "Normal"; + weight = Scale "Weight" 1 800 400; + size = Scale "Point size" 1 100 12; + stretch = Option_string "Stretch" [ + "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", + "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", + "UltraExpanded" + ] "Normal"; + + _flag = join_sep " " [ + "-family", family.item, + "-weight", print weight.value, + "-pointsize", print size.value, + "-style", style.item, + "-stretch", stretch.item]; + } +} + diff --git a/share/nip2/compat/8.6/_predicate.def b/share/nip2/compat/8.6/_predicate.def new file mode 100644 index 00000000..7fae24a8 --- /dev/null +++ b/share/nip2/compat/8.6/_predicate.def @@ -0,0 +1,530 @@ + +/* is_colour_space str: is a string one of nip's colour space names + */ +is_colour_space str = Image_type.colour_spaces.present 0 str; + +/* is_colour_type n: is a number one of VIPS's colour spaces + */ +is_colour_type n = Image_type.colour_spaces.present 1 n; + +/* is_number: is a real or a complex number. + */ +is_number a = is_real a || is_complex a; + +/* is_int: is an integer + */ +is_int a = is_real a && a == (int) a; + +/* is_uint: is an unsigned integer + */ +is_uint a = is_int a && a >= 0; + +/* is_pint: is a positive integer + */ +is_pint a = is_int a && a > 0; + +/* is_preal: is a positive real + */ +is_preal a = is_real a && a > 0; + +/* is_ureal: is an unsigned real + */ +is_ureal a = is_real a && a >= 0; + +/* is_letter c: true if character c is an ASCII letter + * + * is_letter :: char -> bool + */ +is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + +/* is_digit c: true if character c is an ASCII digit + * + * is_digit :: char->bool + */ +is_digit x = '0' <= x && x <= '9'; + +/* A whitespace character. + * + * is_space :: char->bool + */ +is_space = member " \n\t"; + +/* List str starts with section prefix. + * + * is_prefix "hell" "hello world!" == true + * is_prefix :: [*] -> [*] -> bool + */ +is_prefix prefix str = take (len prefix) str == prefix; + +/* List str ends with section suffix. + * + * is_suffix "ld!" "hello world!" == true + * is_suffix :: [*] -> [*] -> bool + */ +is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; + +/* List contains seqence. + * + * is_substr "llo" "hello world!" == true + * is_substr :: [*] -> [*] -> bool + */ +is_substr seq str = any (map (is_prefix seq) (iterate tl str)); + +/* is_listof p s: true if finite list with p true for every element. + */ +is_listof p l = is_list l && all (map p l); + +/* is_string s: true if finite list of char. + */ +is_string s = is_listof is_char s; + +/* is_real_list l: is l a list of real numbers ... test each element, + * so no infinite lists pls. + */ +is_real_list l = is_listof is_real l; + +/* is_string_list l: is l a finite list of finite strings. + */ +is_string_list l = is_listof is_string l; + +/* Test list length ... quicker than len x == n for large lists. + */ +is_list_len n x + = true, x == [] && n == 0 + = false, x == [] || n == 0 + = is_list_len (n - 1) (tl x); + +is_list_len_more n x + = true, x != [] && n == 0 + = false, x == [] || n == 0 + = is_list_len_more (n - 1) (tl x); + +is_list_len_more_equal n x + = true, n == 0 + = false, x == [] + = is_list_len_more_equal (n - 1) (tl x); + +/* is_rectangular l: is l a rectangular data structure + */ +is_rectangular l + = true, !is_list l + = true, all (map is_obj l) + = true, all (map is_list l) && + all (map (not @ is_obj) l) && + all (map is_rectangular l) && + is_list_len_more 0 l && + all (map (is_list_len (len (hd l))) (tl l)) + = false +{ + // treat strings as a base type, not [char] + is_obj x = !is_list x || is_string x; +} + +/* is_matrix l: is l a list of lists of real numbers, all the same length + * + * [[]] is the empty matrix, [] is the empty list ... disallow [] + */ +is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; + +/* is_square_matrix l: is l a matrix with width == height + */ +is_square_matrix l + = true, l == [[]] + = is_matrix l && is_list_len (len (hd l)) l; + +/* is_oddmatrix l: is l a matrix with odd-length sides + */ +is_oddmatrix l + = true, l == [[]] + = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; + +/* is_odd_square_matrix l: is l a square_matrix with odd-length sides + */ +is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; + +/* Is an item in a column of a table? + */ +is_incolumn n table x = member (map (extract n) table) x; + +/* Is HGuide or VGuide. + */ +is_HGuide x = is_instanceof "HGuide" x; + +is_VGuide x = is_instanceof "VGuide" x; + +is_Guide x = is_HGuide x || is_VGuide x; + +is_Mark x = is_instanceof "Mark" x; + +is_Group x = is_instanceof "Group" x; + +is_NULL x = is_instanceof "NULL" x; + +is_List x = is_instanceof "List" x; + +is_Image x = is_instanceof "Image" x; + +is_Plot x = is_instanceof "Plot" x; + +is_Region x = is_instanceof "Region" x; + +is_Real x = is_instanceof "Real" x; + +is_Matrix x = is_instanceof "Matrix_base" x; + +is_Vector x = is_instanceof "Vector" x; + +is_Colour x = is_instanceof "Colour" x; + +is_Arrow x = is_instanceof "Arrow" x; + +is_Bool x = is_instanceof "Bool" x; + +is_Scale x = is_instanceof "Scale" x; + +is_Rect x = is_instanceof "Rect" x; + +is_Number x = is_instanceof "Number" x; + +is_Expression x = is_instanceof "Expression" x; + +is_String x = is_instanceof "String" x; + +/* A list of the form [[1,2],[3,4],[5,6]...] + */ +is_xy_list l + = is_list l && all (map xy l) +{ + xy l = is_real_list l && is_list_len 2 l; +} + +// does a nested list structure contain a Group object? +contains_Group l + = true, is_list l && any (map is_Group l) + = any (map contains_Group l), is_list l + = false; + +/* Does an object have a sensible VIPS type? + */ +has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; + +/* Try to get a VIPS image type from an object. + */ +get_type x + = get_type_im x, is_image x + = get_type_im x.value, is_Image x + = get_type_im x.image.value, is_Arrow x + = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x + // slightly odd ... but our display is always 0-255, so it makes sense for + // a plain number to be in the same range + = Image_type.sRGB, is_real x + = oo_unary_function get_type_op x, is_class x + = error (_ "bad arguments to " ++ "get_type") +{ + get_type_op = Operator "get_type" get_type + Operator_type.COMPOUND false; + + // get the type from a VIPS image ... but only if it makes sense with + // the rest of the image + + // we often have Type set wrong, hence the ugly guessing :-( + // can have alpha, hence we let bands be one more than you might think + + get_type_im im + = Image_type.LABQ, coding == Image_coding.LABPACK + = Image_type.scRGB, coding == Image_coding.RAD + = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 + = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && + (width == 1 || height == 1) + = Image_type.B_W, is_bands 1 + = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 + = type, is_colorimetric && is_bands 3 + = Image_type.sRGB, !is_colorimetric && is_bands 3 + = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 + = type + { + type = get_header "Type" im; + coding = get_header "Coding" im; + bands = get_header "Bands" im; + width = get_header "Xsize" im; + height = get_header "Ysize" im; + + // 3-band colorimetric types we allow ... the things which the + // Colour/Convert To menu can make, excluding mono. + ok_types = [ + Image_type.sRGB, + Image_type.scRGB, + Image_type.RGB16, + Image_type.LAB, + Image_type.LABQ, + Image_type.LABS, + Image_type.LCH, + Image_type.XYZ, + Image_type.YXY, + Image_type.UCS + ]; + is_colorimetric = member ok_types type; + + // is bands n, with an optional alpha (ie. can be n + 1 too) + is_bands n = bands == n || bands == n + 1; + } +} + +has_format x = has_member "format" x || is_Arrow x || is_image x; + +get_format x + = x.format, has_member "format" x + = x.image.format, is_Arrow x + = get_header "BandFmt" x, is_image x + = oo_unary_function get_format_op x, is_class x + = error (_ "bad arguments to " ++ "get_format") +{ + get_format_op = Operator "get_format" get_format + Operator_type.COMPOUND false; +} + +has_bits x = has_member "bits" x || is_Arrow x || is_image x; + +get_bits x + = x.bits, has_member "bits" x + = x.image.bits, is_Arrow x + = get_header "Bbits" x, is_image x + = oo_unary_function get_bits_op x, is_class x + = error (_ "bad arguments to " ++ "get_bits") +{ + get_bits_op = Operator "get_bits" get_format + Operator_type.COMPOUND false; +} + +has_bands x = is_image x || has_member "bands" x || is_Arrow x; + +get_bands x + = x.bands, has_member "bands" x + = x.image.bands, is_Arrow x + = get_header "Bands" x, is_image x + = 1, is_real x + = len x, is_real_list x + = oo_unary_function get_bands_op x, is_class x + = error (_ "bad arguments to " ++ "get_bands") +{ + get_bands_op = Operator "get_bands" get_bands + Operator_type.COMPOUND false; +} + +has_coding x = has_member "coding" x || is_Arrow x || is_image x; + +get_coding x + = x.coding, has_member "coding" x + = x.image.coding, is_Arrow x + = get_header "Coding" x, is_image x + = Image_coding.NOCODING, is_real x + = oo_unary_function get_coding_op x, is_class x + = error (_ "bad arguments to " ++ "get_coding") +{ + get_coding_op = Operator "get_coding" get_coding + Operator_type.COMPOUND false; +} + +has_xres x = has_member "xres" x || is_Arrow x || is_image x; + +get_xres x + = x.xres, has_member "xres" x + = x.image.xres, is_Arrow x + = get_header "Xres" x, is_image x + = oo_unary_function get_xres_op x, is_class x + = error (_ "bad arguments to " ++ "get_xres") +{ + get_xres_op = Operator "get_xres" get_xres + Operator_type.COMPOUND false; +} + +has_yres x = has_member "yres" x || is_Arrow x || is_image x; + +get_yres x + = x.yres, has_member "yres" x + = x.image.yres, is_Arrow x + = get_header "Yres" x, is_image x + = oo_unary_function get_yres_op x, is_class x + = error (_ "bad arguments to " ++ "get_yres") +{ + get_yres_op = Operator "get_yres" get_yres + Operator_type.COMPOUND false; +} + +has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; + +get_xoffset x + = x.xoffset, has_member "xoffset" x + = x.image.xoffset, is_Arrow x + = get_header "Xoffset" x, is_image x + = oo_unary_function get_xoffset_op x, is_class x + = error (_ "bad arguments to " ++ "get_xoffset") +{ + get_xoffset_op = Operator "get_xoffset" get_xoffset + Operator_type.COMPOUND false; +} + +has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; + +get_yoffset x + = x.yoffset, has_member "yoffset" x + = x.image.yoffset, is_Arrow x + = get_header "Yoffset" x, is_image x + = oo_unary_function get_yoffset_op x, is_class x + = error (_ "bad arguments to " ++ "get_yoffset") +{ + get_yoffset_op = Operator "get_yoffset" get_yoffset + Operator_type.COMPOUND false; +} + +has_value = has_member "value"; + +get_value x = x.value; + +has_image x = is_image x || is_Image x || is_Arrow x; + +get_image x + = x.value, is_Image x + = x.image.value, is_Arrow x + = x, is_image x + = oo_unary_function get_image_op x, is_class x + = error (_ "bad arguments to " ++ "get_image") +{ + get_image_op = Operator "get_image" get_image + Operator_type.COMPOUND false; +} + +has_number x = is_number x || is_Real x; + +get_number x + = x.value, is_Real x + = x, is_number x + = oo_unary_function get_number_op x, is_class x + = error (_ "bad arguments to " ++ "get_number") +{ + get_number_op = Operator "get_number" get_number + Operator_type.COMPOUND false; +} + +has_real x = is_real x || is_Real x; + +get_real x + = x.value, is_Real x + = x, is_real x + = oo_unary_function get_real_op x, is_class x + = error (_ "bad arguments to " ++ "get_real") +{ + get_real_op = Operator "get_real" get_real + Operator_type.COMPOUND false; +} + +has_width x = has_member "width" x || is_image x; + +get_width x + = x.width, has_member "width" x + = get_header "Xsize" x, is_image x + = oo_unary_function get_width_op x, is_class x + = error (_ "bad arguments to " ++ "get_width") +{ + get_width_op = Operator "get_width" get_width + Operator_type.COMPOUND false; +} + +has_height x = has_member "height" x || is_image x; + +get_height x + = x.height, has_member "height" x + = get_header "Ysize" x, is_image x + = oo_unary_function get_height_op x, is_class x + = error (_ "bad arguments to " ++ "get_height") +{ + get_height_op = Operator "get_height" get_height + Operator_type.COMPOUND false; +} + +has_left x = has_member "left" x; + +get_left x + = x.left, has_member "left" x + = oo_unary_function get_left_op x, is_class x + = error (_ "bad arguments to " ++ "get_left") +{ + get_left_op = Operator "get_left" get_left + Operator_type.COMPOUND false; +} + +has_top x = has_member "top" x; + +get_top x + = x.top, has_member "top" x + = oo_unary_function get_top_op x, is_class x + = error (_ "bad arguments to " ++ "get_top") +{ + get_top_op = Operator "get_top" get_top + Operator_type.COMPOUND false; +} + +// like has/get member, but first in a lst of objects +has_member_list has objects + = filter has objects != []; + +// need one with the args swapped +get_member = converse dot; + +// get a member from the first of a list of objects to have it +get_member_list has get objects + = hd members, members != [] + = error "unable to get property" +{ + members = map get (filter has objects); +} + +is_hist x + = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) +{ + im = get_image x; + w = get_width im; + h = get_height im; + t = get_type im; +} + +get_header field x + = oo_unary_function get_header_op x, is_class x + = get_header_image x, is_image x + = error (_ "bad arguments to " ++ "get_header") +{ + get_header_op = Operator "get_header" (get_header field) + Operator_type.COMPOUND false; + get_header_image im + = im_header_int field im, type == itype + = im_header_double field im, type == dtype + = im_header_string field im, type == stype1 || type == stype2 + = error (_ "image has no field " ++ field), type == 0 + = error (_ "unknown type for field " ++ field) + { + type = im_header_get_typeof field im; + + itype = name2gtype "gint"; + dtype = name2gtype "gdouble"; + stype1 = name2gtype "VipsRefString"; + stype2 = name2gtype "gchararray"; + } +} + +get_header_type field x + = oo_unary_function get_header_type_op x, is_class x + = im_header_get_typeof field x, is_image x + = error (_ "bad arguments to " ++ "get_header_type") +{ + get_header_type_op = Operator "get_header_type" (get_header_type field) + Operator_type.COMPOUND false; +} + +set_header field value x + = oo_unary_function set_header_op x, is_class x + = im_copy_set_meta x field value, is_image x + = error (_ "bad arguments to " ++ "set_header") +{ + set_header_op = Operator "set_header" (set_header field value) + Operator_type.COMPOUND false; +} diff --git a/share/nip2/compat/8.6/_stdenv.def b/share/nip2/compat/8.6/_stdenv.def new file mode 100644 index 00000000..0112d1f7 --- /dev/null +++ b/share/nip2/compat/8.6/_stdenv.def @@ -0,0 +1,2619 @@ +/* optional args to functions + */ + +get_option options defaults f + = error (_ "unknown parameter " ++ f), hits == [] + = hits?0 +{ + hits = [v :: [n, v] <- options ++ defaults; n == f]; +} + +/* Various operators as functions. + */ + +logical_and a b = a && b; +logical_or a b = a || b; +bitwise_and a b = a & b; +bitwise_or a b = a | b; +eor a b = a ^ b; +left_shift a b = a << b; +right_shift a b = a >> b; +not a = !a; + +less a b = a < b; +more a b = a > b; +less_equal a b = a <= b; +more_equal a b = a >= b; +equal a b = a == b; +not_equal a b = a != b; +pointer_equal a b = a === b; +not_pointer_equal a b = a !== b; + +add a b = a + b; +subtract a b = a - b; +multiply a b = a * b; +divide a b = a / b; +idivide a b = (int) ((int) a / (int) b); +power a b = a ** b; +square x = x * x; +remainder a b = a % b; + +cons a b = a : b; +dot a b = a . ( b ); +join a b = a ++ b; +// 'difference' is defined in _list +subscript a b = a ? b; + +generate s n f = [s, n .. f]; +comma r i = (r, i); + +compose f g = f @ g; + +// our only trinary operator is actually a binary operator +if_then_else a x = if a then x?0 else x?1; + +cast_unsigned_char x = (unsigned char) x; +cast_signed_char x = (signed char) x; +cast_unsigned_short x = (unsigned short) x; +cast_signed_short x = (signed short) x; +cast_unsigned_int x = (unsigned int) x; +cast_signed_int x = (signed int) x; +cast_float x = (float) x; +cast_double x = (double) x; +cast_complex x = (complex) x; +cast_double_complex x = (double complex) x; + +unary_minus x = -x; +negate x = !x; +complement x = ~x; +unary_plus x = +x; + +// the function we call for "a -> v" expressions +mksvpair s v + = [s, v], is_string s + = error "not str on lhs of ->"; + +// the vector ops ... im is an image, vec is a real_list +vec op_name im vec + = im_lintra_vec ones im vec, + op_name == "add" || op_name == "add'" + = im_lintra_vec ones (-1 * im) vec, + op_name == "subtract'" + = im_lintra_vec ones im inv, + op_name == "subtract" + = im_lintra_vec vec im zeros, + op_name == "multiply" || op_name == "multiply'" + = im_lintra_vec vec (1 / im) zeros, + op_name == "divide'" + = im_lintra_vec recip im zeros, + op_name == "divide" + = im_expntra_vec im vec, + op_name == "power'" + = im_powtra_vec im vec, + op_name == "power" + = im_remainderconst_vec im vec, + op_name == "remainder" + = im_andimage_vec im vec, + op_name == "bitwise_and" || op_name == "bitwise_and'" + = im_orimage_vec im vec, + op_name == "bitwise_or" || op_name == "bitwise_or'" + = im_eorimage_vec im vec, + op_name == "eor" || op_name == "eor'" + = im_equal_vec im vec, + op_name == "equal" || op_name == "equal'" + = im_notequal_vec im vec, + op_name == "not_equal" || op_name == "not_equal'" + = im_less_vec im vec, + op_name == "less" + = im_moreeq_vec im vec, + op_name == "less'" + = im_lesseq_vec im vec, + op_name == "less_equal" + = im_more_vec im vec, + op_name == "less_equal'" + = error ("unimplemented vector operation: " ++ op_name) +{ + zeros = replicate (len vec) 0; + ones = replicate (len vec) 1; + recip = map (divide 1) vec; + inv = map (multiply (-1)) vec; +} + +// make a name value pair +mknvpair n v + = [n, v], is_string n + = error "not [char] on LHS of =>"; + +/* Macbeth chart patch names. + */ +macbeth_names = [ + "Dark skin", + "Light skin", + "Blue sky", + "Foliage", + "Blue flower", + "Bluish green", + "Orange", + "Purplish blue", + "Moderate red", + "Purple", + "Yellow green", + "Orange yellow", + "Blue", + "Green", + "Red", + "Yellow", + "Magenta", + "Cyan", + "White (density 0.05)", + "Neutral 8 (density 0.23)", + "Neutral 6.5 (density 0.44)", + "Neutral 5 (density 0.70)", + "Neutral 3.5 (density 1.05)", + "Black (density 1.50)" +]; + +bandsplit x + = oo_unary_function bandsplit_op x, is_class x + = map (subscript x) [0 .. bands - 1], is_image x + = error (_ "bad arguments to " ++ "bandsplit") +{ + bands = get_header "Bands" x; + bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) + Operator_type.COMPOUND false; +} + +bandjoin l + = wrapper joined, has_wrapper + = joined, is_listof has_image l + = error (_ "bad arguments to " ++ "bandjoin") +{ + has_wrapper = has_member_list (has_member "Image") l; + wrapper = get_member_list (has_member "Image") (get_member "Image") l; + joined = im_gbandjoin (map get_image l); +} + +bandand x + = oo_unary_function bandand_op x, is_class x + = foldr1 bitwise_and (bandsplit x), is_image x + = error (_ "bad arguments to " ++ "bandand") +{ + bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; +} + +bandor x + = oo_unary_function bandor_op x, is_class x + = foldr1 bitwise_or (bandsplit x), is_image x + = error (_ "bad arguments to " ++ "bandor") +{ + bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; +} + +sum x + = oo_unary_function sum_op x, is_class x + = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x + = sum_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") +{ + sum_op = Operator "sum" sum Operator_type.COMPOUND false; + + // add elements in a nested-list thing + sum_list l + = foldr acc 0 l + { + acc x total + = total + sum x, is_list x + = total + x; + } +} + +product x + = oo_unary_function product_op x, is_class x + = product_list x, is_list x + // (product image) doesn't make much sense :( + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") +{ + product_op = Operator "product" product Operator_type.COMPOUND false; + + product_list l + = foldr prod 1 l + { + prod x total + = total * product x, is_list x + = total * x; + } +} + +mean x + = oo_unary_function mean_op x, is_class x + = im_avg x, is_image x + = mean_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") +{ + mean_op = Operator "mean" mean Operator_type.COMPOUND false; + + mean_list l = sum l / size l; + + // number of elements in some sort of nested-list thing + size l + = foldr acc 0 l + { + acc x total + = total + size x, is_list x + = total + 1; + } +} + +meang x + = (appl (power e) @ mean @ appl log) x +{ + appl fn x + = map fn x, is_list x + = fn x; +} + +skew x + = oo_unary_function skew_op x, is_class x + = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x + = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") +{ + skew_op = Operator "skew" skew Operator_type.COMPOUND false; + + // squash any large matrix down to a flat list ... much simpler + x' + = x, is_image x; + = flatten x; + + m = mean x'; + s = deviation x'; + w = get_width x'; + h = get_height x'; + b = get_bands x'; + + N + = w * h * b, is_image x' + = len x'; +} + +kurtosis x + = oo_unary_function kurtosis_op x, is_class x + = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x + = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") +{ + kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; + + // squash any large matrix down to a flat list ... much simpler + x' + = x, is_image x; + = flatten x; + + m = mean x'; + s = deviation x'; + w = get_width x'; + h = get_height x'; + b = get_bands x'; + + N + = len x', is_list x'; + = w * h * b; +} + +// zero-excluding mean +meanze x + = oo_unary_function meanze_op x, is_class x + = meanze_image_hist x, is_image x && + (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) + = meanze_image x, is_image x + = meanze_list x, is_list x + = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") +{ + fmt = get_format x; + + meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; + + meanze_list l = sum l / size l; + + // number of non-zero elements in some sort of nested-list thing + size l + = foldr acc 0 l + { + acc x total + = total + size x, is_list x + = total + 1, x != 0; + = total; + } + + // add elements in a nested-list thing + sum l + = foldr acc 0 l + { + acc x total + = total + sum x, is_list x + = total + x; + } + + // image mean, for any image type + meanze_image i + = sum / N + { + w = get_width i; + h = get_height i; + b = get_bands i; + + st = stats i; + sum = st.value?0?2; + + // find non-zero pixels (not zero in all bands) + zp = im_notequal_vec i (replicate b 0); + + // number of non-zero pixels + N = b * (mean zp * w * h) / 255; + } + + // image mean for 8 and 16-bit unsigned images + // we can use a histogram, yay, and save a pass through the image + meanze_image_hist i + = sum / N + { + // histogram, knock out zeros + hist = hist_find i; + black = image_new 1 1 (get_bands hist) + (get_format hist) (get_coding hist) (get_type hist) 0 0 0; + histze = insert 0 0 black hist; + + // matching identity + iden + = im_identity_ushort (get_bands hist) (get_width hist), + (get_width hist) > 256 + = im_identity (get_bands hist); + + // number of non-zero pixels + N = mean histze * 256; + + // sum of pixels + sum = mean (hist * iden) * 256; + } +} + +deviation x + = oo_unary_function deviation_op x, is_class x + = im_deviate x, is_image x + = deviation_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "deviation") +{ + deviation_op = Operator "deviation" + deviation Operator_type.COMPOUND false; + + deviation_list l + = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 + { + [n, s, s2] = sum_sum2_list l; + } + + // return n, sum, sum of squares for a list of reals + sum_sum2_list x + = foldr accumulate [0, 0, 0] x + { + accumulate x sofar + = [n + 1, x + s, x * x + s2], is_real x + = [n + n', s + s', s2 + s2'], is_list x + = error "sum_sum2_list: not real or [real]" + { + [n, s, s2] = sofar; + [n', s', s2'] = sum_sum2_list x; + } + } +} + +deviationze x + = oo_unary_function deviationze_op x, is_class x + = deviationze_image x, is_image x + = deviationze_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "deviationze") +{ + deviationze_op = Operator "deviationze" + deviationze Operator_type.COMPOUND false; + + deviationze_list l + = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 + { + [n, s, s2] = sum_sum2_list l; + } + + // return number of non-zero elements, sum, sum of squares for a list of + // reals + sum_sum2_list x + = foldr accumulate [0, 0, 0] x + { + accumulate x sofar + = sofar, is_real x && x == 0 + = [n + 1, x + s, x * x + s2], is_real x + = [n + n', s + s', s2 + s2'], is_list x + = error "sum_sum2_list: not real or [real]" + { + [n, s, s2] = sofar; + [n', s', s2'] = sum_sum2_list x; + } + } + + deviationze_image i + = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 + { + w = get_width i; + h = get_height i; + b = get_bands i; + + st = stats i; + sum = st.value?0?2; + sum2 = st.value?0?3; + + // find non-zero pixels (not zero in all bands) + zp = im_notequal_vec i (replicate b 0); + + // number of non-zero pixels + N = b * (mean zp * w * h) / 255; + } +} + +// find the centre of gravity of a histogram +gravity x + = oo_unary_function gravity_op x, is_class x + = im_hist_gravity x, is_hist x + = gravity_list x, is_list x + = error (_ "bad arguments to " ++ "gravity") +{ + gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; + + // centre of gravity of a histogram... use the histogram to weight an + // identity, then sum, then find the mean element + im_hist_gravity h + = m + { + // make horizontal + h' + = rot270 h, get_width h == 1 + = h, get_height h == 1 + = error "width or height not 1"; + + // number of elements + w = get_width h'; + + // matching identity + i + = im_identity_ushort 1 w, w <= 2 ** 16 - 1 + = make_xy w 1 ? 0; + + // weight identity and sum + s = mean (i * h') * w; + + // sum of original histogram + s' = mean h * w; + + // weighted mean + m = s / s'; + } + + gravity_list l + = m + { + w = len l; + + // matching identity + i = [0, 1 .. w - 1]; + + // weight identity and sum + s = sum (map2 multiply i l); + + // sum of original histogram + s' = sum l; + + // weighted mean + m = s / s'; + } +} + +project x + = oo_unary_function project_op x, is_class x + = im_project x, is_image x + = error (_ "bad arguments to " ++ "project") +{ + project_op = Operator "project" project Operator_type.COMPOUND false; +} + +abs x + = oo_unary_function abs_op x, is_class x + = im_abs x, is_image x + = abs_cmplx x, is_complex x + = abs_num x, is_real x + = abs_list x, is_real_list x + = abs_list (map abs_list x), is_matrix x + = error (_ "bad arguments to " ++ "abs") +{ + abs_op = Operator "abs" abs Operator_type.COMPOUND false; + + abs_list l = (sum (map square l)) ** 0.5; + + abs_num n + = n, n >= 0 + = -n; + + abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; +} + +copy x + = oo_unary_function copy_op x, is_class x + = im_copy x, is_image x + = x +{ + copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; +} + +// like abs, but treat pixels as vectors ... ie. always get a 1-band image +// back ... also treat matricies as lists of vectors +// handy for dE from object difference +abs_vec x + = oo_unary_function abs_vec_op x, is_class x + = abs_vec_image x, is_image x + = abs_vec_cmplx x, is_complex x + = abs_vec_num x, is_real x + = abs_vec_list x, is_real_list x + = mean (map abs_vec_list x), is_matrix x + = error (_ "bad arguments to " ++ "abs_vec") +{ + abs_vec_op = Operator "abs_vec" + abs_vec Operator_type.COMPOUND false; + + abs_vec_list l = (sum (map square l)) ** 0.5; + + abs_vec_num n + = n, n >= 0 + = -n; + + abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; + + abs_vec_image im + = (sum (map square (bandsplit im))) ** 0.5; +} + +transpose x + = oo_unary_function transpose_op x, is_class x + = transpose_image x, is_image x + = transpose_list x, is_listof is_list x + = error (_ "bad arguments to " ++ "transpose") +{ + transpose_op = Operator "transpose" + transpose Operator_type.COMPOUND_REWRAP false; + + transpose_list l + = [], l' == [] + = (map hd l') : (transpose_list (map tl l')) + { + l' = takewhile (not_equal []) l; + } + + transpose_image = im_flipver @ im_rot270; +} + +rot45 x + = oo_unary_function rot45_op x, is_class x + = error "rot45 image: not implemented", is_image x + = error (_ "bad arguments to " ++ "rot45") +{ + rot45_op = Operator "rot45" + rot45_object Operator_type.COMPOUND_REWRAP false; + + rot45_object x + = rot45_matrix x, is_odd_square_matrix x + = error "rot45 image: not implemented", is_image x + = error (_ "bad arguments to " ++ "rot45"); + + // slow, but what the heck + rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; +} + +// apply an image function to a [[real]] ... matrix is converted to a 1 band +// image for processing +apply_matrix_as_image fn m + = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; + +// a general image/matrix operation where the mat version is most easily done +// by converting mat->image->mat +apply_matim_operation name fn x + = oo_unary_function class_op x, is_class x + = fn x, is_image x + = apply_matrix_as_image fn x, is_matrix x + = error (_ "bad arguments to " ++ name) +{ + class_op = Operator name + (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; +} + +rot90 = apply_matim_operation "rot90" im_rot90; +rot180 = apply_matim_operation "rot180" im_rot180; +rot270 = apply_matim_operation "rot270" im_rot270; +rotquad = apply_matim_operation "rotquad" im_rotquad; +fliplr = apply_matim_operation "fliplr" im_fliphor; +fliptb = apply_matim_operation "flipud" im_flipver; + +image_set_type type x + = oo_unary_function image_set_type_op x, is_class x + = im_copy_set x (to_real type) + (get_header "Xres" x) (get_header "Yres" x) + (get_header "Xoffset" x) (get_header "Yoffset" x), + is_image x + = error (_ "bad arguments to " ++ "image_set_type:" ++ + print type ++ " " ++ print x) +{ + image_set_type_op = Operator "image_set_type" + (image_set_type type) Operator_type.COMPOUND_REWRAP false; +} + +image_set_origin xoff yoff x + = oo_unary_function image_set_origin_op x, is_class x + = im_copy_set x + (get_header "Type" x) + (get_header "Xres" x) (get_header "Yres" x) + (to_real xoff) (to_real yoff), + is_image x + = error (_ "bad arguments to " ++ "image_set_origin") +{ + image_set_origin_op = Operator "image_set_origin" + (image_set_origin xoff yoff) + Operator_type.COMPOUND_REWRAP false; +} + +cache tile_width tile_height max_tiles x + = oo_unary_function cache_op x, is_class x + = im_tile_cache_random x (to_real tile_width) (to_real tile_height) + (to_real max_tiles), is_image x + = error (_ "bad arguments to " ++ "cache") +{ + cache_op = Operator "cache" + (cache tile_width tile_height max_tiles) + Operator_type.COMPOUND_REWRAP false; +} + +tile across down x + = oo_unary_function tile_op x, is_class x + = im_replicate x (to_real across) (to_real down), is_image x + = error (_ "bad arguments to " ++ "tile") +{ + tile_op = Operator "tile" + (tile across down) Operator_type.COMPOUND_REWRAP false; +} + +grid tile_height across down x + = oo_unary_function grid_op x, is_class x + = im_grid x (to_real tile_height) (to_real across) (to_real down), + is_image x + = error (_ "bad arguments to " ++ "grid") +{ + grid_op = Operator "grid" + (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; +} + +max_pair a b + = a, a > b + = b; + +min_pair a b + = a, a < b + = b; + +range min value max = min_pair max (max_pair min value); + +max x + = oo_unary_function max_op x, is_class x + = im_max x, is_image x + = max_list x, is_list x + = x, is_number x + = error (_ "bad arguments to " ++ "max") +{ + max_op = Operator "max" max Operator_type.COMPOUND false; + + max_list x + = error "max []", x == [] + = foldr1 max_pair x, is_real_list x + = foldr1 max_pair (map max_list x), is_list x + = max x; +} + +min x + = oo_unary_function min_op x, is_class x + = im_min x, is_image x + = min_list x, is_list x + = x, is_number x + = error (_ "bad arguments to " ++ "min") +{ + min_op = Operator "min" min Operator_type.COMPOUND false; + + min_list x + = error "min []", x == [] + = foldr1 min_pair x, is_real_list x + = foldr1 min_pair (map min_list x), is_list x + = min x; +} + +maxpos x + = oo_unary_function maxpos_op x, is_class x + = im_maxpos x, is_image x + = maxpos_matrix x, is_matrix x + = maxpos_list x, is_list x + = error (_ "bad arguments to " ++ "maxpos") +{ + maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; + + maxpos_matrix m + = (-1, -1), m == [[]] + = (indexes?row, row) + { + max_value = max (Matrix m); + indexes = map (index (equal max_value)) m; + row = index (not_equal (-1)) indexes; + } + + maxpos_list l + = -1, l == [] + = index (equal (max l)) l; +} + +minpos x + = oo_unary_function minpos_op x, is_class x + = im_minpos x, is_image x + = minpos_matrix x, is_matrix x + = minpos_list x, is_list x + = error (_ "bad arguments to " ++ "minpos") +{ + minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; + + minpos_matrix m + = (-1, -1), m == [[]] + = (indexes?row, row) + { + min_value = min (Matrix m); + indexes = map (index (equal min_value)) m; + row = index (not_equal (-1)) indexes; + } + + minpos_list l + = -1, l == [] + = index (equal (min l)) l; +} + +stats x + = oo_unary_function stats_op x, is_class x + = im_stats x, is_image x + = im_stats (to_image x).value, is_matrix x + = error (_ "bad arguments to " ++ "stats") +{ + stats_op = Operator "stats" + stats Operator_type.COMPOUND false; +} + +e = 2.7182818284590452354; + +pi = 3.14159265358979323846; + +rad d = 2 * pi * (d / 360); + +deg r = 360 * r / (2 * pi); + +sign x + = oo_unary_function sign_op x, is_class x + = im_sign x, is_image x + = sign_cmplx x, is_complex x + = sign_num x, is_real x + = error (_ "bad arguments to " ++ "sign") +{ + sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; + + sign_num n + = 0, n == 0 + = 1, n > 0 + = -1; + + sign_cmplx c + = (0, 0), mod == 0 + = (re c / mod, im c / mod) + { + mod = abs c; + } +} + +rint x + = oo_unary_function rint_op x, is_class x + = im_rint x, is_image x + = rint_value x, is_number x + = error (_ "bad arguments to " ++ "rint") +{ + rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; + + rint_value x + = (int) (x + 0.5), x > 0 + = (int) (x - 0.5); +} + +scale x + = oo_unary_function scale_op x, is_class x + = (unsigned char) x, is_number x + = im_scale x, is_image x + = scale_list x, is_real_list x || is_matrix x + = error (_ "bad arguments to " ++ "scale") +{ + scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; + + scale_list l + = apply_scale s o l + { + mn = find_limit min_pair l; + mx = find_limit max_pair l; + s = 255.0 / (mx - mn); + o = -(mn * s); + } + + find_limit fn l + = find_limit fn (map (find_limit fn) l), is_listof is_list l + = foldr1 fn l; + + apply_scale s o x + = x * s + o, is_number x + = map (apply_scale s o) x; +} + +scaleps x + = oo_unary_function scale_op x, is_class x + = im_scaleps x, is_image x + = error (_ "bad arguments to " ++ "scale") +{ + scale_op = Operator "scaleps" + scaleps Operator_type.COMPOUND_REWRAP false; +} + +fwfft x + = oo_unary_function fwfft_op x, is_class x + = im_fwfft x, is_image x + = error (_ "bad arguments to " ++ "fwfft") +{ + fwfft_op = Operator "fwfft" + fwfft Operator_type.COMPOUND_REWRAP false; +} + +invfft x + = oo_unary_function invfft_op x, is_class x + = im_invfftr x, is_image x + = error (_ "bad arguments to " ++ "invfft") +{ + invfft_op = Operator "invfft" + invfft Operator_type.COMPOUND_REWRAP false; +} + +falsecolour x + = oo_unary_function falsecolour_op x, is_class x + = image_set_type Image_type.sRGB (im_falsecolour x), is_image x + = error (_ "bad arguments to " ++ "falsecolour") +{ + falsecolour_op = Operator "falsecolour" + falsecolour Operator_type.COMPOUND_REWRAP false; +} + +polar x + = oo_unary_function polar_op x, is_class x + = im_c2amph x, is_image x + = polar_cmplx x, is_complex x + = error (_ "bad arguments to " ++ "polar") +{ + polar_op = Operator "polar" polar Operator_type.COMPOUND false; + + polar_cmplx r + = (l, a) + { + a + = 270, x == 0 && y < 0 + = 90, x == 0 && y >= 0 + = 360 + atan (y / x), x > 0 && y < 0 + = atan (y / x), x > 0 && y >= 0 + = 180 + atan (y / x); + + l = (x ** 2 + y ** 2) ** 0.5; + + x = re r; + y = im r; + } +} + +rectangular x + = oo_unary_function rectangular_op x, is_class x + = im_c2rect x, is_image x + = rectangular_cmplx x, is_complex x + = error (_ "bad arguments to " ++ "rectangular") +{ + rectangular_op = Operator "rectangular" + rectangular Operator_type.COMPOUND false; + + rectangular_cmplx p + = (x, y) + { + l = re p; + a = im p; + + x = l * cos a; + y = l * sin a; + } +} + +// we can't use colour_unary: that likes 3 band only +recomb matrix x + = oo_unary_function recomb_op x, is_class x + = im_recomb x matrix, is_image x + = recomb_real_list x, is_real_list x + = map recomb_real_list x, is_matrix x + = error (_ "bad arguments to " ++ "recomb") +{ + // COMPOUND_REWRAP ... signal to the colour class to go to image and + // back + recomb_op = Operator "recomb" + (recomb matrix) Operator_type.COMPOUND_REWRAP false; + + // process [1,2,3 ..] as an image + recomb_real_list l + = (to_matrix im').value?0 + { + im = (float) (to_image (Vector l)).value; + im' = recomb matrix im; + } +} + +extract_area x y w h obj + = oo_unary_function extract_area_op obj, is_class obj + = im_extract_area obj x' y' w' h', is_image obj + = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj + = error (_ "bad arguments to " ++ "extract_area") +{ + x' = to_real x; + y' = to_real y; + w' = to_real w; + h' = to_real h; + + extract_area_op = Operator "extract_area" (extract_area x y w h) + Operator_type.COMPOUND_REWRAP false; + + extract_range from length list + = (take length @ drop from) list; +} + +extract_band b obj = subscript obj b; + +extract_row y obj + = oo_unary_function extract_row_op obj, is_class obj + = extract_area 0 y' (get_width obj) 1 obj, is_image obj + = [obj?y'], is_matrix obj + = error (_ "bad arguments to " ++ "extract_row") +{ + y' = to_real y; + + extract_row_op = Operator "extract_row" (extract_row y) + Operator_type.COMPOUND_REWRAP false; +} + +extract_column x obj + = oo_unary_function extract_column_op obj, is_class obj + = extract_area x' 0 1 height obj, is_image obj + = map (\row [row?x']) obj, is_matrix obj + = error (_ "bad arguments to " ++ "extract_column") +{ + x' = to_real x; + height = get_header "Ysize" obj; + + extract_column_op = Operator "extract_column" (extract_column x) + Operator_type.COMPOUND_REWRAP false; +} + +blend cond in1 in2 + = oo_binary_function blend_op cond [in1,in2], is_class cond + = im_blend (get_image cond) (get_image in1) (get_image in2), + has_image cond && has_image in1 && has_image in2 + = error (_ "bad arguments to " ++ "blend" ++ ": " ++ + join_sep ", " (map print [cond, in1, in2])) +{ + blend_op = Operator "blend" + blend_obj Operator_type.COMPOUND_REWRAP false; + + blend_obj cond x + = blend_result_image + { + [then_part, else_part] = x; + + // get things about our output from inputs in this order + objects = [then_part, else_part, cond]; + + // properties of our output image + target_width = get_member_list has_width get_width objects; + target_height = get_member_list has_height get_height objects; + target_bands = get_member_list has_bands get_bands objects; + target_format = get_member_list has_format get_format objects; + target_type = get_member_list has_type get_type objects; + + to_image x + = to_image_size target_width target_height + target_bands target_format x; + + [then_image, else_image] = map to_image [then_part, else_part]; + + blend_result_image = image_set_type target_type + (im_blend cond then_image else_image); + } +} + +// do big first: we want to keep big's class, if possible +// eg. big is a Plot, small is a 1x1 Image +insert x y small big + = oo_binary'_function insert_op small big, is_class big + = oo_binary_function insert_op small big, is_class small + = im_insert big small (to_real x) (to_real y), + is_image small && is_image big + = error (_ "bad arguments to " ++ "insert") +{ + insert_op = Operator "insert" + (insert x y) Operator_type.COMPOUND_REWRAP false; +} + +insert_noexpand x y small big + = oo_binary_function insert_noexpand_op small big, is_class small + = oo_binary'_function insert_noexpand_op small big, is_class big + = im_insert_noexpand big small (to_real x) (to_real y), + is_image small && is_image big + = error (_ "bad arguments to " ++ "insert_noexpand") +{ + insert_noexpand_op = Operator "insert_noexpand" + (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; +} + +decode im + = oo_unary_function decode_op im, is_class im + = decode_im im, is_image im + = error (_ "bad arguments to " ++ "decode") +{ + decode_op = Operator "decode" + decode Operator_type.COMPOUND_REWRAP false; + + decode_im im + = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK + = im_rad2float im, get_coding im == Image_coding.RAD + = im; +} + +measure_draw across down measure image + = mark +{ + patch_width = image.width / across; + sample_width = patch_width * (measure / 100); + left_margin = (patch_width - sample_width) / 2; + patch_height = image.height / down; + sample_height = patch_height * (measure / 100); + top_margin = (patch_height - sample_height) / 2; + + cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: + y <- [0 .. down - 1]; x <- [0 .. across - 1]]; + x = map (extract 0) cods; + y = map (extract 1) cods; + + outer = mkim [$pixel => 255] sample_width sample_height 1; + inner = mkim [] (sample_width - 4) (sample_height - 4) 1; + patch = insert 2 2 inner outer; + + bg = mkim [] image.width image.height 1; + + mask = Image (im_insertset bg.value patch.value x y); + + image' = colour_transform_to Image_type.sRGB image; + + mark = if mask then Vector [0, 255, 0] else image'; +} + +measure_sample across down measure image + = measures +{ + patch_width = image.width / across; + sample_width = patch_width * (measure / 100); + left_margin = (patch_width - sample_width) / 2; + patch_height = image.height / down; + sample_height = patch_height * (measure / 100); + top_margin = (patch_height - sample_height) / 2; + + cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: + y <- [0 .. down - 1]; x <- [0 .. across - 1]]; + + image' = decode image; + patches = map (\p extract_area p?0 p?1 sample_width sample_height image') + cods; + measures = Matrix (map (map mean) (map bandsplit patches)); +} + +extract_bands b n obj + = oo_unary_function extract_bands_op obj, is_class obj + = im_extract_bands obj (to_real b) (to_real n), is_image obj + = error (_ "bad arguments to " ++ "extract_bands") +{ + extract_bands_op = Operator "extract_bands" + (extract_bands b n) Operator_type.COMPOUND_REWRAP false; +} + +invert x + = oo_unary_function invert_op x, is_class x + = im_invert x, is_image x + = 255 - x, is_real x + = error (_ "bad arguments to " ++ "invert") +{ + invert_op = Operator "invert" invert Operator_type.COMPOUND false; +} + +transform ipol wrap params image + = oo_unary_function transform_op image, is_class image + = im_transform image + (to_matrix params) (to_real ipol) (to_real wrap), is_image image + = error (_ "bad arguments to " ++ "transform") +{ + transform_op = Operator "transform" + (transform ipol wrap params) + Operator_type.COMPOUND_REWRAP false; +} + +transform_search max_error max_iterations order ipol wrap sample reference + = oo_binary_function transform_search_op sample reference, is_class sample + = oo_binary'_function transform_search_op sample reference, + is_class reference + = im_transform_search sample reference + (to_real max_error) (to_real max_iterations) (to_real order) + (to_real ipol) (to_real wrap), + is_image sample && is_image reference + = error (_ "bad arguments to " ++ "transform_search") +{ + transform_search_op = Operator "transform_search" + (transform_search max_error max_iterations order ipol wrap) + Operator_type.COMPOUND false; +} + +rotate interp angle image + = oo_binary_function rotate_op angle image, is_class angle + = oo_binary'_function rotate_op angle image, is_class image + = im_affinei_all image interp.value a (-b) b a 0 0, + is_real angle && is_image image + = error (_ "bad arguments to " ++ "rotate") +{ + rotate_op = Operator "rotate" + (rotate interp) Operator_type.COMPOUND_REWRAP false; + a = cos angle; + b = sin angle; +} + +matrix_binary fn a b + = itom (fn (mtoi a) (mtoi b)) +{ + mtoi x = im_mask2vips (Matrix x); + itom x = (im_vips2mask x).value; +} + +join_lr a b + = oo_binary_function join_lr_op a b, is_class a + = oo_binary'_function join_lr_op a b, is_class b + = join_im a b, is_image a && is_image b + = matrix_binary join_im a b, is_matrix a && is_matrix b + = error (_ "bad arguments to " ++ "join_lr") +{ + join_lr_op = Operator "join_lr" + join_lr Operator_type.COMPOUND_REWRAP false; + + join_im a b = insert (get_width a) 0 b a; +} + +join_tb a b + = oo_binary_function join_tb_op a b, is_class a + = oo_binary'_function join_tb_op a b, is_class b + = join_im a b, is_image a && is_image b + = matrix_binary join_im a b, is_matrix a && is_matrix b + = error (_ "bad arguments to " ++ "join_tb") +{ + join_tb_op = Operator "join_tb" + join_tb Operator_type.COMPOUND_REWRAP false; + + join_im a b = insert 0 (get_height a) b a; +} + +conj x + = oo_unary_function conj_op x, is_class x + = (re x, -im x), + is_complex x || + (is_image x && format == Image_format.COMPLEX) || + (is_image x && format == Image_format.DPCOMPLEX) + // assume it's some sort of real + = x +{ + format = get_header "BandFmt" x; + conj_op = Operator "conj" conj Operator_type.COMPOUND false; +} + +clip2fmt format image + = oo_unary_function clip2fmt_op image, is_class image + = im_clip2fmt image (to_real format), is_image image + = error (_ "bad arguments to " ++ "clip2fmt") +{ + clip2fmt_op = Operator "clip2fmt" + (clip2fmt format) Operator_type.COMPOUND_REWRAP false; +} + +embed type x y w h im + = oo_unary_function embed_op im, is_class im + = im_embed im (to_real type) + (to_real x) (to_real y) (to_real w) (to_real h), is_image im + = error (_ "bad arguments to " ++ "embed") +{ + embed_op = Operator "embed" + (embed type x y w h) Operator_type.COMPOUND_REWRAP false; +} + +/* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it + * with m1, turn it back to a matrix again. + */ +_morph_2_masks fn m1 m2 + = m'' +{ + // The [[real]] can contain 128 values ... squeeze them out! + image = im_mask2vips (Matrix m2) == 255; + m2_width = get_width image; + m2_height = get_height image; + + // need to embed m2 in an image large enough for us to be able to + // position m1 all around the edges, with a 1 pixel overlap + image' = embed 0 + (m1.width / 2) (m1.height / 2) + (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) + image; + + // morph! + image'' = fn m1 image'; + + // back to mask + m' = im_vips2mask ((double) image''); + + // Turn 0 in output to 128 (don't care). + m'' + = map (map fn) m'.value + { + fn a + = 128, a == 0; + = a; + } +} + +dilate mask image + = oo_unary_function dilate_op image, is_class image + = im_dilate image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "dilate") +{ + dilate_op = Operator "dilate" + dilate_object Operator_type.COMPOUND_REWRAP false; + + dilate_object x + = _morph_2_masks dilate mask x, is_matrix x + = dilate mask x; +} + +erode mask image + = oo_unary_function erode_op image, is_class image + = im_erode image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "erode") +{ + erode_op = Operator "erode" + erode_object Operator_type.COMPOUND_REWRAP false; + + erode_object x + = _morph_2_masks erode mask x, is_matrix x + = erode mask x; +} + +conv mask image + = oo_unary_function conv_op image, is_class image + = im_conv image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "conv" ++ ": " ++ + "conv (" ++ print mask ++ ") (" ++ print image ++ ")") +{ + conv_op = Operator "conv" + (conv mask) Operator_type.COMPOUND_REWRAP false; +} + +convf mask image + = oo_unary_function convf_op image, is_class image + = im_conv_f image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convf" ++ ": " ++ + "convf (" ++ print mask ++ ") (" ++ print image ++ ")") +{ + convf_op = Operator "convf" + (convf mask) Operator_type.COMPOUND_REWRAP false; +} + +convsep mask image + = oo_unary_function convsep_op image, is_class image + = im_convsep image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convsep") +{ + convsep_op = Operator "convsep" + (convsep mask) Operator_type.COMPOUND_REWRAP false; +} + +aconvsep layers mask image + = oo_unary_function aconvsep_op image, is_class image + = im_aconvsep image (to_matrix mask) (to_real layers), is_image image + = error (_ "bad arguments to " ++ "aconvsep") +{ + aconvsep_op = Operator "aconvsep" + (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; +} + +convsepf mask image + = oo_unary_function convsepf_op image, is_class image + = im_convsep_f image (to_matrix mask), is_image image + = error (_ "bad arguments to " ++ "convsepf") +{ + convsepf_op = Operator "convsepf" + (convsepf mask) Operator_type.COMPOUND_REWRAP false; +} + +rank w h n image + = oo_unary_function rank_op image, is_class image + = im_rank image (to_real w) (to_real h) (to_real n), is_image image + = error (_ "bad arguments to " ++ "rank") +{ + rank_op = Operator "rank" + (rank w h n) Operator_type.COMPOUND_REWRAP false; +} + +rank_image n x + = rlist x.value, is_Group x + = rlist x, is_list x + = error (_ "bad arguments to " ++ "rank_image") +{ + rlist l + = wrapper ranked, has_wrapper + = ranked + { + has_wrapper = has_member_list (has_member "Image") l; + wrapper = get_member_list (has_member "Image") (get_member "Image") l; + ranked = im_rank_image (map get_image l) (to_real n); + } +} + +// find the correlation surface for a small image within a big one +correlate small big + = oo_binary_function correlate_op small big, is_class small + = oo_binary'_function correlate_op small big, is_class big + = im_spcor big small, is_image small && is_image big + = error (_ "bad arguments to " ++ "correlate") +{ + correlate_op = Operator "correlate" + correlate Operator_type.COMPOUND_REWRAP false; +} + +// just sum-of-squares-of-differences +correlate_fast small big + = oo_binary_function correlate_fast_op small big, is_class small + = oo_binary'_function correlate_fast_op small big, is_class big + = im_fastcor big small, is_image small && is_image big + = error (_ "bad arguments to " ++ "correlate_fast") +{ + correlate_fast_op = Operator "correlate_fast" + correlate_fast Operator_type.COMPOUND_REWRAP false; +} + +// set Type, wrap as Plot_hist if it's a class +hist_tag x + = oo_unary_function hist_tag_op x, is_class x + = image_set_type Image_type.HISTOGRAM x, is_image x + = error (_ "bad arguments to " ++ "hist_tag") +{ + hist_tag_op = Operator "hist_tag" + (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; +} + +hist_find x + = oo_unary_function hist_find_op x, is_class x + = im_histgr x (-1), is_image x + = error (_ "bad arguments to " ++ "hist_find") +{ + hist_find_op = Operator "hist_find" + (Plot_histogram @ hist_find) Operator_type.COMPOUND false; +} + +hist_find_nD bins image + = oo_unary_function hist_find_nD_op image, is_class image + = im_histnD image (to_real bins), is_image image + = error (_ "bad arguments to " ++ "hist_find_nD") +{ + hist_find_nD_op = Operator "hist_find_nD" + (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; +} + +hist_find_indexed mode index value + = oo_binary_function hist_find_indexed_op index value, is_class index + = oo_binary'_function hist_find_indexed_op index value, is_class value + = indexed index value, is_image index && is_image value + = error (_ "bad arguments to " ++ "hist_find_indexed") +{ + hist_find_indexed_op = Operator "hist_find_indexed" + (compose (compose Plot_histogram) (hist_find_indexed mode)) + Operator_type.COMPOUND false; + + indexed index value + = out + { + [out] = vips_call "hist_find_indexed" [value, index] [ + "combine" => mode + ]; + } +} + +hist_entropy x + = oo_unary_function hist_entropy_op x, is_class x + = entropy x, is_image x + = error (_ "bad arguments to " ++ "hist_entropy") +{ + hist_entropy_op = Operator "hist_entropy" + hist_entropy Operator_type.COMPOUND_REWRAP false; + + entropy x + = out + { + [out] = vips_call "hist_entropy" [x] [ + ]; + } +} + +hist_map hist image + = oo_binary_function hist_map_op hist image, is_class hist + = oo_binary'_function hist_map_op hist image, is_class image + = im_maplut image hist, is_image hist && is_image image + = error (_ "bad arguments to " ++ "hist_map") +{ + // can't use rewrap, as we want to always wrap as image + hist_map_op = Operator "hist_map" + (compose (compose Image) hist_map) Operator_type.COMPOUND false; +} + +hist_cum hist + = oo_unary_function hist_cum_op hist, is_class hist + = im_histcum hist, is_image hist + = error (_ "bad arguments to " ++ "hist_cum") +{ + hist_cum_op = Operator "hist_cum" + hist_cum Operator_type.COMPOUND_REWRAP false; +} + +hist_diff hist + = oo_unary_function hist_diff_op hist, is_class hist + = im_histdiff hist, is_image hist + = error (_ "bad arguments to " ++ "hist_diff") +{ + hist_diff_op = Operator "hist_diff" + hist_diff Operator_type.COMPOUND_REWRAP false; + + im_histdiff h + = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h + { + // up the format so it can represent the whole range of + // possible values from this mask + fmt x + = Image_format.SHORT, + x == Image_format.UCHAR || x == Image_format.CHAR + = Image_format.INT, + x == Image_format.USHORT || x == Image_format.SHORT || + x == Image_format.UINT + = x; + } +} + +hist_norm hist + = oo_unary_function hist_norm_op hist, is_class hist + = im_histnorm hist, is_image hist + = error (_ "bad arguments to " ++ "hist_norm") +{ + hist_norm_op = Operator "hist_norm" + hist_norm Operator_type.COMPOUND_REWRAP false; +} + +hist_inv hist + = oo_unary_function hist_inv_op hist, is_class hist + = inv hist, is_image hist + = error (_ "bad arguments to " ++ "hist_inv") +{ + hist_inv_op = Operator "hist_inv" + hist_inv Operator_type.COMPOUND_REWRAP false; + + inv im + = im_invertlut (to_matrix im''') len + { + // need a vertical doublemask + im' + = rot90 im, get_width im > 1 && get_height im == 1 + = im, get_width im == 1 && get_height im > 1 + = error (_ "not a hist"); + len = get_height im'; + + // values must be scaled to 0 - 1 + im'' = im' / (max im'); + + // add an index column on the left + // again, must be in 0-1 + y = ((make_xy 1 len)?1) / len; + im''' = y ++ im''; + } +} + +hist_match in ref + = oo_binary_function hist_match_op in ref, is_class in + = oo_binary'_function hist_match_op in ref, is_class ref + = im_histspec in ref, is_image in && is_image ref + = error (_ "bad arguments to " ++ "hist_match") +{ + hist_match_op = Operator "hist_match" + hist_match Operator_type.COMPOUND_REWRAP false; +} + +hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; + +hist_equalize_local w h l image + = oo_unary_function hist_equalize_local_op image, is_class image + = out, is_image image + = error (_ "bad arguments to " ++ "hist_equalize_local") +{ + hist_equalize_local_op = Operator "hist_equalize_local" + (hist_equalize_local w h l) Operator_type.COMPOUND_REWRAP false; + + [out] = vips_call "hist_local" + [image, to_real w, to_real h] [$max_slope => to_real l]; +} + +// find the threshold below which are percent of the image (percent in [0,1]) +// eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels +hist_thresh percent image + = x +{ + // our own normaliser ... we don't want to norm channels separately + // norm to [0,1] + my_hist_norm h = h / max h; + + // normalised cumulative hist + // we sum the channels before we normalise, because we want to treat them + // all the same + h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) + image.value; + + // threshold that, then use im_profile to search for the x position in the + // histogram + x = mean (im_profile (h > percent) 1); +} + +/* Sometimes useful, despite plotting now being built in, for making + * diagnostic images. + */ +hist_plot hist + = oo_unary_function hist_plot_op hist, is_class hist + = im_histplot hist, is_image hist + = error (_ "bad arguments to " ++ "hist_plot") +{ + hist_plot_op = Operator "hist_plot" + (Image @ hist_plot) Operator_type.COMPOUND false; +} + +zerox d x + = oo_unary_function zerox_op x, is_class x + = im_zerox x (to_real d), is_image x + = error (_ "bad arguments to " ++ "zerox") +{ + zerox_op = Operator "zerox" + (zerox d) Operator_type.COMPOUND_REWRAP false; +} + +reduce kernel xshr yshr image + = oo_unary_function reduce_op image, is_class image + = reduce_im image, is_image image + = error (_ "bad arguments to " ++ "reduce") +{ + reduce_op = Operator "reduce" + reduce_im Operator_type.COMPOUND_REWRAP false; + + reduce_im im + = out + { + [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; + } +} + +similarity interpolate scale angle image + = oo_unary_function similarity_op image, is_class image + = similarity_im image, is_image image + = error (_ "bad arguments to " ++ "similarity") +{ + similarity_op = Operator "similarity" + similarity_im Operator_type.COMPOUND_REWRAP false; + + similarity_im im + = out + { + [out] = vips_call "similarity" [im] [ + $interpolate => interpolate.value, + $scale => scale, + $angle => angle + ]; + } +} + +resize kernel xfac yfac image + = oo_unary_function resize_op image, is_class image + = resize_im image, is_image image + = error (_ "bad arguments to " ++ "resize") +{ + resize_op = Operator "resize" + resize_im Operator_type.COMPOUND_REWRAP false; + + xfac' = to_real xfac; + yfac' = to_real yfac; + + rxfac' = 1 / xfac'; + ryfac' = 1 / yfac'; + + is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; + + resize_im im + // upscale by integer factor, nearest neighbour + = im_zoom im xfac' yfac', + is_int xfac' && is_int yfac' && + xfac' >= 1 && yfac' >= 1 && + is_nn + + // downscale by integer factor, nearest neighbour + = im_subsample im rxfac' ryfac', + is_int rxfac' && is_int ryfac' && + rxfac' >= 1 && ryfac' >= 1 && + is_nn + + // everything else ... we just pass on to vips_resize() + = vips_resize kernel xfac' yfac' im + { + vips_resize kernel hscale vscale im + = out + { + [out] = vips_call "resize" [im, hscale] + [$vscale => vscale, $kernel => kernel.value]; + } + } +} + +sharpen radius x1 y2 y3 m1 m2 in + = oo_unary_function sharpen_op in, is_class in + = im_sharpen in (to_real radius) + (to_real x1) (to_real y2) (to_real y3) + (to_real m1) (to_real m2), is_image in + = error (_ "bad arguments to " ++ "sharpen") +{ + sharpen_op = Operator "sharpen" + (sharpen radius x1 y2 y3 m1 m2) + Operator_type.COMPOUND_REWRAP false; +} + +tone_analyse s m h sa ma ha in + = oo_unary_function tone_analyse_op in, is_class in + = im_tone_analyse in + (to_real s) (to_real m) (to_real h) + (to_real sa) (to_real ma) (to_real ha), is_image in + = error (_ "bad arguments to " ++ "tone_analyse") +{ + tone_analyse_op = Operator "tone_analyse" + (Plot_histogram @ tone_analyse s m h sa ma ha) + Operator_type.COMPOUND false; +} + +tone_map hist image + = oo_binary_function tone_map_op hist image, is_class hist + = oo_binary'_function tone_map_op hist image, is_class image + = im_tone_map image hist, is_image hist && is_image image + = error (_ "bad arguments to " ++ "tone_map") +{ + tone_map_op = Operator "tone_map" + tone_map Operator_type.COMPOUND_REWRAP false; +} + +tone_build fmt b w s m h sa ma ha + = (Plot_histogram @ clip2fmt fmt) + (im_tone_build_range mx mx + (to_real b) (to_real w) + (to_real s) (to_real m) (to_real h) + (to_real sa) (to_real ma) (to_real ha)) +{ + mx = Image_format.maxval fmt; +} + +icc_export depth profile intent in + = oo_unary_function icc_export_op in, is_class in + = im_icc_export_depth in + (to_real depth) (expand profile) (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_export") +{ + icc_export_op = Operator "icc_export" + (icc_export depth profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_import profile intent in + = oo_unary_function icc_import_op in, is_class in + = im_icc_import in + (expand profile) (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_import") +{ + icc_import_op = Operator "icc_import" + (icc_import profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_import_embedded intent in + = oo_unary_function icc_import_embedded_op in, is_class in + = im_icc_import_embedded in (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_import_embedded") +{ + icc_import_embedded_op = Operator "icc_import_embedded" + (icc_import_embedded intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_transform in_profile out_profile intent in + = oo_unary_function icc_transform_op in, is_class in + = im_icc_transform in + (expand in_profile) (expand out_profile) + (to_real intent), is_image in + = error (_ "bad arguments to " ++ "icc_transform") +{ + icc_transform_op = Operator "icc_transform" + (icc_transform in_profile out_profile intent) + Operator_type.COMPOUND_REWRAP false; +} + +icc_ac2rc profile in + = oo_unary_function icc_ac2rc_op in, is_class in + = im_icc_ac2rc in (expand profile), is_image in + = error (_ "bad arguments to " ++ "icc_ac2rc") +{ + icc_ac2rc_op = Operator "icc_ac2rc" + (icc_ac2rc profile) + Operator_type.COMPOUND_REWRAP false; +} + + +// Given a xywh rect, flip it around so wh are always positive +rect_normalise x y w h + = [x', y', w', h'] +{ + x' + = x + w, w < 0 + = x; + y' + = y + h, h < 0 + = y; + w' = abs w; + h' = abs h; +} + +draw_flood_blob x y ink image + = oo_unary_function draw_flood_blob_op image, is_class image + = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image + = error (_ "bad arguments to " ++ "draw_flood_blob") +{ + draw_flood_blob_op = Operator "draw_flood_blob" + (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_flood x y ink image + = oo_unary_function draw_flood_op image, is_class image + = im_draw_flood image (to_real x) (to_real y) ink, is_image image + = error (_ "bad arguments to " ++ "draw_flood") +{ + draw_flood_op = Operator "draw_flood" + (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; +} + +/* This version of draw_rect uses insert_noexpand and will be fast, even for + * huge images. + */ +draw_rect_width x y w h f t ink image + = oo_unary_function draw_rect_width_op image, is_class image + = my_draw_rect_width image (to_int x) (to_int y) + (to_int w) (to_int h) (to_int f) (to_int t) ink, + is_image image + = error (_ "bad arguments to " ++ "draw_rect_width") +{ + draw_rect_width_op = Operator "draw_rect_width" + (draw_rect_width x y w h f t ink) + Operator_type.COMPOUND_REWRAP false; + + my_draw_rect_width image x y w h f t ink + = insert x' y' (block w' h') image, f == 1 + = (insert x' y' (block w' t) @ + insert (x' + w' - t) y' (block t h') @ + insert x' (y' + h' - t) (block w' t) @ + insert x' y' (block t h')) image + { + insert = insert_noexpand; + block w h = image_new w h (get_bands image) (get_format image) + (get_coding image) (get_type image) ink' 0 0; + ink' + = Vector ink, is_list ink + = ink; + [x', y', w', h'] = rect_normalise x y w h; + } +} + +/* Default to 1 pixel wide edges. + */ +draw_rect x y w h f ink image + = draw_rect_width x y w h f 1 ink image; + +/* This version of draw_rect uses the paintbox rect draw operation. It is an + * inplace operation and will use bucketloads of memory. + */ +draw_rect_paintbox x y w h f ink image + = oo_unary_function draw_rect_op image, is_class image + = im_draw_rect image (to_real x) (to_real y) + (to_real w) (to_real h) (to_real f) ink, is_image image + = error (_ "bad arguments to " ++ "draw_rect_paintbox") +{ + draw_rect_op = Operator "draw_rect" + (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_circle x y r f ink image + = oo_unary_function draw_circle_op image, is_class image + = im_draw_circle image (to_real x) (to_real y) + (to_real r) (to_real f) ink, is_image image + = error (_ "bad arguments to " ++ "draw_circle") +{ + draw_circle_op = Operator "draw_circle" + (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; +} + +draw_line x1 y1 x2 y2 ink image + = oo_unary_function draw_line_op image, is_class image + = im_draw_line image (to_real x1) (to_real y1) + (to_real x2) (to_real y2) ink, is_image image + = error (_ "bad arguments to " ++ "draw_line") +{ + draw_line_op = Operator "draw_line" + (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; +} + +print_base base in + = oo_unary_function print_base_op in, is_class in + = map (print_base base) in, is_list in + = print_base_real, is_real in + = error (_ "bad arguments to " ++ "print_base") +{ + print_base_op + = Operator "print_base" (print_base base) Operator_type.COMPOUND false; + + print_base_real + = error "print_base: bad base", base < 2 || base > 16 + = "0", in < 0 || chars == [] + = reverse chars + { + digits = map (\x x % base) + (takewhile (not_equal 0) + (iterate (\x idivide x base) in)); + chars = map tohd digits; + + tohd x + = (char) ((int) '0' + x), x < 10 + = (char) ((int) 'A' + (x - 10)); + } +} + +/* id x: the identity function + * + * id :: * -> * + */ +id x = x; + +/* const x y: junk y, return x + * + * (const 3) is the function that always returns 3. + * const :: * -> ** -> * + */ +const x y = x; + +/* converse fn a b: swap order of args to fn + * + * converse fn a b == fn b a + * converse :: (* -> ** -> ***) -> ** -> * -> *** + */ +converse fn a b = fn b a; + +/* fix fn x: find the fixed point of a function + */ +fix fn x = limit (iterate fn x); + +/* until pred fn n: apply fn to n until pred succeeds; return that value + * + * until (more 1000) (multiply 2) 1 = 1024 + * until :: (* -> bool) -> (* -> *) -> * -> * + */ +until pred fn n + = n, pred n + = until pred fn (fn n); + +/* Infinite list of primes. + */ +primes + = 1 : (sieve [2 ..]) +{ + sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); + nmultiple n x = x / n != (int) (x / n); +} + +/* Map an n-ary function (pass the args as a list) over groups of objects. + * The objects can be single objects or groups. If more than one + * object is a group, we iterate for the length of the smallest group. + * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. + * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the + * output and don't apply the function. + + copy-pasted into _types, keep in sync + + */ +map_nary fn args + = fn args, groups == [] + = Group (map process [0, 1 .. shortest - 1]) +{ + // find all the group arguments + groups = filter is_Group args; + + // what's the length of the shortest group arg? + shortest = foldr1 min_pair (map (len @ get_value) groups); + + // process index n ... pull that member from each argument + // recurse to handle application, so we work for nested groups too + process n + = NULL, any (map (is_noval n) args) + = map_nary fn (map (extract n) args) + { + extract n arg + = arg.value?n, is_Group arg + = arg; + + is_noval n arg = is_Group arg && arg.value?n == NULL; + } +} + +/* Map a 1-ary function over an object. + */ +map_unary fn a = map_nary (list_1ary fn) [a]; + +/* Map a 2-ary function over a pair of objects. + */ +map_binary fn a b = map_nary (list_2ary fn) [a, b]; + +/* Map a 3-ary function over three objects. + */ +map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; + +/* Map a 4-ary function over three objects. + */ +map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; + +/* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on + * vectors and matricies. + */ +map_nary_list fn args + = fn args, lists == [] + = map process [0, 1 .. shortest - 1] +{ + // find all the list arguments + lists = filter is_list args; + + // what's the length of the shortest list arg? + shortest = foldr1 min_pair (map len lists); + + // process index n ... pull that member from each argument + // recurse to handle application, so we work for nested lists too + process n + = map_nary_list fn (map (extract n) args) + { + extract n arg + = arg?n, is_list arg + = arg; + } +} + +map_unaryl fn a = map_nary_list (list_1ary fn) [a]; + +map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; + +/* Remove features smaller than x pixels across from an image. This used to be + * rather complex ... convsep is now good enough to use. + */ +smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; + +/* Chop up an image into a list of lists of smaller images. Pad edges with + * black. + */ +imagearray_chop tile_width tile_height hoverlap voverlap i + = map chop' [0, vstep .. last_y] +{ + width = get_width i; + height = get_height i; + bands = get_bands i; + format = get_format i; + type = get_type i; + + tile_width' = to_real tile_width; + tile_height' = to_real tile_height; + hoverlap' = to_real hoverlap; + voverlap' = to_real voverlap; + + /* Unique pixels per tile. + */ + hstep = tile_width' - hoverlap'; + vstep = tile_height' - voverlap'; + + /* Number of tiles across and down. Remember the case where width == + * hstep. + */ + across = (int) ((width - 1) / hstep) + 1; + down = (int) ((height - 1) / vstep) + 1; + + /* top/left of final tile. + */ + last_x = hstep * (across - 1); + last_y = vstep * (down - 1); + + /* How much do we need to pad by? + */ + sx = last_x + tile_width'; + sy = last_y + tile_height'; + + /* Expand image with black to pad size. + */ + pad = embed 0 0 0 sx sy i; + + /* Chop up a row. + */ + chop' y + = map chop'' [0, hstep .. last_x] + { + chop'' x = extract_area x y tile_width' tile_height' pad; + } +} + +/* Reassemble image. + */ +imagearray_assemble hoverlap voverlap il + = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il +{ + lrj l r = insert (get_width l + hoverlap) 0 r l; + tbj t b = insert 0 (get_height t + voverlap) b t; +} + +/* Generate an nxn identity matrix. + */ +identity_matrix n + = error "identity_matrix: n > 0", n < 1 + = map line [0 .. n - 1] +{ + line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; +} + +/* Incomplete gamma function Q(a, x) == 1 - P(a, x) + + FIXME ... this is now a builtin, until we can get a nice List class + (requires overloadable ':') + +gammq a x + = error "bad args", x < 0 || a <= 0 + = 1 - gamser, x < a + 1 + = gammcf +{ + gamser = (gser a x)?0; + gammcf = (gcf a x)?0; +} + */ + +/* Incomplete gamma function P(a, x) evaluated as series representation. Also + * return ln(gamma(a)) ... nr in c, pp 218 + */ +gser a x + = [gamser, gln] +{ + gln = gammln a; + gamser + = error "bad args", x < 0 + = 0, x == 0 + = 1 // fix this + { + // maximum iterations + maxit = 100; + + ap = List [a + 1, a + 2 ...]; + xoap = x / ap; + + del = map product (prefixes xoap.value); + + + + + + + +/* + del = map (multiply (1 / a)) (map product (prefixes xoap)) + + del = + + xap = iterate (multiply + */ + + /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, + * 3], [1, 2, 3, 4] ...] + */ + prefixes l = map (converse take l) [1..]; + + } +} + +/* ln(gamma(xx)) ... nr in c, pp 214 + */ +gammln xx + = gln +{ + cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, + -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; + y = take 6 (iterate (add 1) (xx + 1)); + ser = 1.000000000190015 + sum (map2 divide cof y); + tmp = xx + 0.5; + tmp' = tmp - ((xx + 0.5) * log tmp); + gln = -tmp + log (2.5066282746310005 * ser / xx); +} + +/* make a LUT from a scatter + */ +buildlut x + = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 + = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 + = error (_ "bad arguments to " ++ "buildlut"); + +/* Linear regression. Return a class with the stuff we need in. + * from s15.2, p 665 NR in C + * + * Also calculate R2, see eg.: + * https://en.wikipedia.org/wiki/Coefficient_of_determination + */ +linreg xes yes + = obj +{ + obj = class { + // in case we ever get shown in the workspace + _vislevel = 2; + + slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; + intercept = (sy - sx * slope) / ss; + + chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; + + siga = (chi2 / (ss - 2)) ** 0.5 * + ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; + sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; + + // for compat with linregw, see below + q = 1.0; + + R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; + } + + ss = len xes; + sx = sum xes; + sy = sum yes; + my = sy / ss; + sxoss = sx / ss; + + tes = [x - sxoss :: x <- xes]; + st2 = sum [t ** 2 :: t <- tes]; +} + +/* Weighted linear regression. Xes, yes and a list of deviations. + */ +linregw xes yes devs + = obj +{ + obj = class { + // in case we ever get shown in the workspace + _vislevel = 2; + + slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; + intercept = (sy - sx * slope) / ss; + + chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: + [x, y, sd] <- zip3 xes yes devs]; + + siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; + sigb = (1 / st2) ** 0.5; + + q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); + + R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; + } + + wt = [sd ** -0.5 :: sd <- devs]; + + ss = sum wt; + sx = sum [x * w :: [x, w] <- zip2 xes wt]; + sy = sum [y * w :: [y, w] <- zip2 yes wt]; + my = sy / len xes; + sxoss = sx / ss; + + tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; + st2 = sum [t ** 2 :: t <- tes]; +} + +/* Clustering: pass in a list of points, repeatedly merge the + * closest two points until no two points are closer than the threshold. + * Return [merged-points, corresponding-weights]. A weight is a list of the + * indexes we merged to make that point, ie. len weight == how significant + * this point is. + * + * eg. + * cluster 12 [152,154,155,42,159] == + * [[155,42],[[1,2,0,4],[3]]] + */ +cluster thresh points + = oo_unary_function cluster_op points, is_class points + // can't use [0..len points - 1], in case len points == 0 + = merge [points, map (converse cons []) (take (len points) [0 ..])], + is_list points + = error (_ "bad arguments to " ++ "cluster") +{ + cluster_op = Operator "cluster" + (cluster thresh) Operator_type.COMPOUND false; + + merge x + = x, m < 2 || d > thresh + = merge [points', weights'] + { + [points, weights] = x; + m = len points; + + // generate indexes of all possible pairs, avoiding comparing a thing + // to itself, and assuming that dist is reflexive + // first index is always less than 2nd index + // the +1,+2 makes sure we have an increasing generator, otherwise we + // can get [3 .. 4] (for example), which will make a decreasing + // sequence + pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; + + // distance function + // arg is eg. [3,1], meaning get distance from point 3 to point 1 + dist x + = abs (points?i - points?j) + { + [i, j] = x; + } + + // smallest distance, then the two points we merge + p = minpos (map dist pairs); + d = dist pairs?p; + [i, j] = pairs?p; + + // new point and new weight + nw = weights?i ++ weights?j; + np = (points?i * len weights?i + points?j * len weights?j) / len nw; + + // remove element i from a list + remove i l = take i l ++ drop (i + 1) l; + + // remove two old points, add the new merged one + // i < j (see "pairs", above) + points' = np : remove i (remove j points); + weights' = nw : remove i (remove j weights); + } +} + +/* Extract the area of an image around an arrow. + * Transform the image to make the arrow horizontal, then displace by hd and + * vd pxels, then cut out a bit h pixels high centered on the arrow. + */ +extract_arrow hd vd h arrow + = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' +{ + // the line as a polar vector + pv = polar (arrow.width, arrow.height); + a = im pv; + + // smallest rotation that will make the line horizontal + a' + = 360 - a, a > 270 + = 180 - a, a > 90 + = -a; + + im' = rotate Interpolate_bilinear a' arrow.image; + + // look at the start and end of the arrow, pick the leftmost + p + = (arrow.left, arrow.top), arrow.left <= arrow.right + = (arrow.right, arrow.bottom); + + // transform that point to im' space + p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); +} + +/* You'd think these would go in _convert, but they are not really colour ops, + * so put them here. + */ +rad2float image + = oo_unary_function rad2float_op image, is_class image + = im_rad2float image, is_image image + = error (_ "bad arguments to " ++ "rad2float") +{ + rad2float_op = Operator "rad2float" + rad2float Operator_type.COMPOUND_REWRAP false; +} + +float2rad image + = oo_unary_function float2rad_op image, is_class image + = im_float2rad image, is_image image + = error (_ "bad arguments to " ++ "float2rad") +{ + float2rad_op = Operator "float2rad" + float2rad Operator_type.COMPOUND_REWRAP false; +} + +segment x + = oo_unary_function segment_op x, is_class x + = image', is_image x + = error (_ "bad arguments to " ++ "segment") +{ + segment_op = Operator "segment" + segment Operator_type.COMPOUND_REWRAP false; + + [image, nsegs] = im_segment x; + image' = im_copy_set_meta image "n-segments" nsegs; +} + +point a b + = oo_binary_function point_op a b, is_class a + = oo_binary'_function point_op a b, is_class b + = im_read_point b x y, is_image b + = [b?x?y], is_matrix b + = [b?x], is_real_list b && y == 0 + = [b?y], is_real_list b && x == 0 + = error (_ "bad arguments to " ++ "point") +{ + point_op = Operator "point" + (\a\b Vector (point a b)) Operator_type.COMPOUND false; + + (x, y) + = a, is_complex a; + = (a?0, a?1), is_real_list a && is_list_len 2 a + = error "bad position format"; +} + +/* One image in, one out. + */ +system_image command x + = oo_unary_function system_image_op x, is_class x + = system x, is_image x + = error (_ "bad arguments to " ++ "system_image") +{ + system_image_op = Operator "system_image" + (system_image command) Operator_type.COMPOUND_REWRAP false; + + system im + = image_out + { + [image_out, log] = + im_system_image (get_image im) "%s.tif" "%s.tif" command; + } +} + +/* Two images in, one out. + */ +system_image2 command x1 x2 + = oo_binary_function system_image2_op x1 x2, is_class x1 + = oo_binary'_function system_image2_op x1 x2, is_class x2 + = system x1 x2, is_image x1 && is_image x2 + = error (_ "bad arguments to " ++ "system_image2") +{ + system_image2_op = Operator "system_image2" + (system_image2 command) Operator_type.COMPOUND_REWRAP false; + + system x1 x2 + = image_out + { + [image_out] = vips_call "system" [command] [ + $in => [x1, x2], + $out => true, + $out_format => "%s.tif", + $in_format => "%s.tif" + ]; + } +} + +/* Three images in, one out. + */ +system_image3 command x1 x2 x3 + = oo_binary_function system_image2_op x2 x3, is_class x2 + = oo_binary'_function system_image2_op x2 x3, is_class x3 + = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 + = error (_ "bad arguments to " ++ "system_image3") +{ + system_image2_op = Operator "system_image2" + (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; + + system x1 x2 x3 + = image_out + { + [image_out] = vips_call "system" [command] [ + $in => [x1, x2, x3], + $out => true, + $out_format => "%s.tif", + $in_format => "%s.tif" + ]; + } +} + +/* Zero images in, one out. + */ +system_image0 command + = Image image_out +{ + [image_out] = vips_call "system" [command] [ + $out => true, + $out_format => "%s.tif" + ]; +} + +hough_line w h x + = oo_unary_function hough_line_op x, is_class x + = hline (to_real w) (to_real h) x, is_image x + = error (_ "bad arguments to " ++ "hough_line") +{ + hough_line_op = Operator "hough_line" + (hough_line w h) Operator_type.COMPOUND_REWRAP false; + + hline w h x + = pspace + { + [pspace] = vips_call "hough_line" [x] [ + $width => w, + $height => h + ]; + } +} + +hough_circle s mn mx x + = oo_unary_function hough_circle_op x, is_class x + = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x + = error (_ "bad arguments to " ++ "hough_circle") +{ + hough_circle_op = Operator "hough_circle" + (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; + + hcircle s mn mx x + = pspace + { + [pspace] = vips_call "hough_circle" [x] [ + $scale => s, + $min_radius => mn, + $max_radius => mx + ]; + } +} + +mapim interp ind in + = oo_binary_function mapim_op ind in, is_class ind + = oo_binary'_function mapim_op ind in, is_class in + = mapim_fn ind in, is_image ind && is_image in + = error (_ "bad arguments to " ++ "mapim") +{ + mapim_op = Operator "mapim" + (mapim interp) Operator_type.COMPOUND_REWRAP false; + + mapim_fn ind im + = out + { + [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; + } +} + +perlin cell width height + = Image im +{ + [im] = vips_call "perlin" [to_real width, to_real height] [ + $cell_size => to_real cell + ]; +} + +worley cell width height + = Image im +{ + [im] = vips_call "worley" [to_real width, to_real height] [ + $cell_size => to_real cell + ]; +} + +gaussnoise width height mean sigma + = im +{ + [im] = vips_call "gaussnoise" [to_real width, to_real height] [ + $mean => to_real mean, + $sigma => to_real sigma + ]; +} + +flattenimage bg x + = oo_unary_function flatten_op x, is_class x + = flt (to_vector bg) x, is_image x + = error (_ "bad arguments to " ++ "flattenimage") +{ + flatten_op = Operator "flatten" + (flattenimage bg) Operator_type.COMPOUND_REWRAP false; + + flt bg x + = out + { + [out] = vips_call "flatten" [x] [ + $background => bg.value + ]; + } +} + +premultiply x + = oo_unary_function premultiply_op x, is_class x + = prem x, is_image x + = error (_ "bad arguments to " ++ "premultiply") +{ + premultiply_op = Operator "premultiply" + premultiply Operator_type.COMPOUND_REWRAP false; + + prem x + = out + { + [out] = vips_call "premultiply" [x] [ + ]; + } +} + +unpremultiply x + = oo_unary_function unpremultiply_op x, is_class x + = unprem x, is_image x + = error (_ "bad arguments to " ++ "unpremultiply") +{ + unpremultiply_op = Operator "unpremultiply" + unpremultiply Operator_type.COMPOUND_REWRAP false; + + unprem x + = out + { + [out] = vips_call "unpremultiply" [x] [ + ]; + } +} + +hist_entropy x + = oo_unary_function hist_entropy_op x, is_class x + = entropy x, is_image x + = error (_ "bad arguments to " ++ "hist_entropy") +{ + hist_entropy_op = Operator "hist_entropy" + hist_entropy Operator_type.COMPOUND_REWRAP false; + + entropy x + = out + { + [out] = vips_call "hist_entropy" [x] [ + ]; + } +} diff --git a/share/nip2/compat/8.6/_types.def b/share/nip2/compat/8.6/_types.def new file mode 100644 index 00000000..d07712f6 --- /dev/null +++ b/share/nip2/compat/8.6/_types.def @@ -0,0 +1,1410 @@ +/* A list of things. Do automatic iteration of unary and binary operators on + * us. + * List [1, 2] + [2, 3] -> List [3, 5] + * hd (List [2, 3]) -> 2 + * List [] == [] -> true + */ +List value = class + _Object { + _check_args = [ + [value, "value", check_list] + ]; + + // methods + oo_binary_table op x = [ + [apply2 op value x', + op.op_name == "subscript" || op.op_name == "subscript'" || + op.op_name == "equal" || op.op_name == "equal'"], + [this.List (apply2 op value x'), + op.op_name == "join" || op.op_name == "join'"], + [this.List (map2 (apply2 op) value x'), + is_list x'], + [this.List (map (apply2 op' x) value), + true] + ] ++ super.oo_binary_table op x + { + op' = oo_converse op; + + // strip the List wrapper, if any + x' + = x.value, is_List x + = x; + + apply2 op x1 x2 + = oo_binary_function op x1 x2, is_class x1 + = oo_binary'_function op x1 x2, is_class x2 + = op.fn x1 x2; + }; + + oo_unary_table op = [ + [apply value, + op.op_name == "hd" || op.op_name == "tl"], + [this.List (map apply value), + true] + ] ++ super.oo_unary_table op + { + apply x + = oo_unary_function op x, is_class x + = op.fn x; + } +} + +/* A group of things. Loop the operation over the group. + */ +Group value = class + _Object { + _check_args = [ + [value, "value", check_list] + ]; + + // methods + oo_binary_table op x = [ + // if_then_else is really a trinary operator + [map_trinary ite this x?0 x?1, + op.op_name == "if_then_else"], + [map_binary op.fn this x, + is_Group x], + [map_unary (\a op.fn a x) this, + true] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [map_unary op.fn this, + true] + ] ++ super.oo_unary_table op; + + // we can't call map_trinary directly, since it uses Group and we + // don't support mutually recursive top-level functions :-( + // copy-paste it here, keep in sync with the version in _stdenv + map_nary fn args + = fn args, groups == [] + = Group (map process [0, 1 .. shortest - 1]) + { + groups = filter is_Group args; + + shortest = foldr1 min_pair (map (len @ get_value) groups); + + process n + = NULL, any (map (is_noval n) args) + = map_nary fn (map (extract n) args) + { + extract n arg + = arg.value?n, is_Group arg + = arg; + + is_noval n arg = is_Group arg && arg.value?n == NULL; + } + } + + // need ite as a true trinary + ite a b c = if a then b else c; + + map_unary fn a = map_nary (list_1ary fn) [a]; + map_binary fn a b = map_nary (list_2ary fn) [a, b]; + map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; +} + +/* Single real number ... eg slider. + */ +Real value = class + _Object { + _check_args = [ + [value, "value", check_real] + ]; + + // methods + oo_binary_table op x = [ + [this.Real (op.fn this.value x.value), + is_Real x && + op.type == Operator_type.ARITHMETIC], + [this.Real (op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC], + [op.fn this.value x.value, + is_Real x && + op.type == Operator_type.RELATIONAL], + [op.fn this.value x, + !is_class x] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [this.Real (op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* Single bool ... eg Toggle. + */ +Bool value = class + _Object { + _check_args = [ + [value, "value", check_bool] + ]; + + // methods + oo_binary_table op x = [ + [op.fn this.value x, + op.op_name == "if_then_else"], + [this.Bool (op.fn this.value x.value), + is_Bool x], + [this.Bool (op.fn this.value x), + is_bool x] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [this.Bool (op.fn this.value), + op.type == Operator_type.ARITHMETIC || + op.type == Operator_type.RELATIONAL], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* An editable string. + */ +String caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* An editable real number. + */ +Number caption value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string] + ]; + + Real x = this.Number caption x; +} + +/* An editable expression. + */ +Expression caption expr = class + (if is_class expr then expr else _Object) { + _check_args = [ + [caption, "caption", check_string], + [expr, "expr", check_any] + ]; +} + +/* A ticking clock. + */ +Clock interval value = class + scope.Real value { + _check_args = [ + [interval, "interval", check_real] + ]; + + Real x = this.Clock interval x; +} + +/* An editable filename. + */ +Pathname caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* An editable fontname. + */ +Fontname caption value = class + _Object { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_string] + ]; +} + +/* Vector type ... just a finite list of real. Handy for wrapping an + * argument to eg. im_lintra_vec. Make it behave like a single pixel image. + */ +Vector value = class + _Object { + _check_args = [ + [value, "value", check_real_list] + ]; + + bands = len value; + + // methods + oo_binary_table op x = [ + // Vector ++ Vector means bandwise join + [this.Vector (op.fn this.value x.value), + is_Vector x && + (op.op_name == "join" || op.op_name == "join'")], + [this.Vector (op.fn this.value [get_number x]), + has_number x && + (op.op_name == "join" || op.op_name == "join'")], + // Vector ? number means extract element + [op.fn this.value (get_real x), + has_real x && + (op.op_name == "subscript" || + op.op_name == "subscript'")], + // extra check for lengths equal + [this.Vector (map_binaryl op.fn this.value x.value), + is_Vector x && + len value == len x.value && + op.type == Operator_type.ARITHMETIC], + [this.Vector (map_binaryl op.fn this.value (get_real x)), + has_real x && + op.type == Operator_type.ARITHMETIC], + + // need extra length check + [this.Vector (map bool_to_real + (map_binaryl op.fn this.value x.value)), + is_Vector x && + len value == len x.value && + op.type == Operator_type.RELATIONAL], + [this.Vector (map bool_to_real + (map_binaryl op.fn this.value (get_real x))), + has_real x && + op.type == Operator_type.RELATIONAL], + [this.Vector (op.fn this.value x.value), + is_Vector x && + len value == len x.value && + op.type == Operator_type.COMPOUND_REWRAP], + [x.Image (vec op'.op_name x.value value), + is_Image x], + [vec op'.op_name x value, + is_image x], + [op.fn this.value x, + is_real x] + ] ++ super.oo_binary_table op x + { + op' = oo_converse op; + }; + + oo_unary_table op = [ + [this.Vector (map_unaryl op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [this.Vector (map bool_to_real + (map_unaryl op.fn this.value)), + op.type == Operator_type.RELATIONAL], + [this.Vector (op.fn this.value), + op.type == Operator_type.COMPOUND_REWRAP], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; + + // turn an ip bool (or a number, for Vector) into VIPSs 255/0 + bool_to_real x + = 255, is_bool x && x + = 255, is_number x && x != 0 + = 0; +} + +/* A rectangular array of real. + */ +Matrix_base value = class + _Object { + _check_args = [ + [value, "value", check_matrix] + ]; + + // calculate these from value + width = len value?0; + height = len value; + + // extract a rectanguar area + extract left top width height + = this.Matrix_base + ((map (take width) @ map (drop left) @ + take height @ drop top) value); + + // methods + oo_binary_table op x = [ + // mat multiply is special + [this.Matrix_base mul.value, + is_Matrix x && + op.op_name == "multiply"], + [this.Matrix_base mul'.value, + is_Matrix x && + op.op_name == "multiply'"], + + // mat divide is also special + [this.Matrix_base div.value, + is_Matrix x && + op.op_name == "divide"], + [this.Matrix_base div'.value, + is_Matrix x && + op.op_name == "divide'"], + + // power -1 means invert + [this.Matrix_base inv.value, + is_real x && x == -1 && + op.op_name == "power"], + [this.Matrix_base sq.value, + is_real x && x == 2 && + op.op_name == "power"], + [error "matrix **-1 and **2 only", + op.op_name == "power" || + op.op_name == "power'"], + + // matrix op vector ... treat a vector as a 1 row matrix + [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), + is_Vector x && + op.type == Operator_type.ARITHMETIC], + [this.Matrix_base (map_binaryl op.fn this.value x.value), + (is_Matrix x || is_Real x) && + op.type == Operator_type.ARITHMETIC], + + [this.Matrix_base (map_binaryl op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC], + + // compound ... don't do iteration + [this.Matrix_base (op.fn this.value x.value), + (is_Matrix x || is_Real x || is_Vector x) && + op.type == Operator_type.COMPOUND_REWRAP], + + [op.fn this.value x, + op.type == Operator_type.COMPOUND] + + ] ++ super.oo_binary_table op x + { + mul = im_matmul this x; + mul' = im_matmul x this; + div = im_matmul this (im_matinv x); + div' = im_matmul x (im_matinv this); + inv = im_matinv this; + sq = im_matmul this this; + op' = oo_converse op; + } + + oo_unary_table op = [ + [this.Matrix_base (map_unaryl op.fn this.value), + op.type == Operator_type.ARITHMETIC], + [this.Matrix_base (op.fn this.value), + op.type == Operator_type.COMPOUND_REWRAP], + [op.fn this.value, + true] + ] ++ super.oo_unary_table op; +} + +/* How to display a matrix: text, sliders, toggles, or text plus scale/offset. + */ +Matrix_display = class { + text = 0; + slider = 1; + toggle = 2; + text_scale_offset = 3; + + is_display = member [text, slider, toggle, text_scale_offset]; +} + +/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add + * a display type as well to control how the widget renders. + */ +Matrix_vips value scale offset filename display = class + scope.Matrix_base value { + _check_args = [ + [scale, "scale", check_real], + [offset, "offset", check_real], + [filename, "filename", check_string], + [display, "display", check_matrix_display] + ]; + + Matrix_base x = this.Matrix_vips x scale offset filename display; +} + +/* A plain 'ol matrix which can be passed to VIPS. + */ +Matrix value = class + Matrix_vips value 1 0 "" Matrix_display.text {} + +/* Specialised constructors ... for convolutions, recombinations and + * morphologies. + */ +Matrix_con scale offset value = class + Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; + +Matrix_rec value = class + Matrix_vips value 1 0 "" Matrix_display.slider {}; + +Matrix_mor value = class + Matrix_vips value 1 0 "" Matrix_display.toggle {}; + +Matrix_file filename = (im_read_dmask @ expand @ search) filename; + +/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) + */ +Colour colour_space value = class + scope.Vector value { + _check_args = [ + [colour_space, "colour_space", check_colour_space] + ]; + _check_all = [ + [is_list_len 3 value, "len value == 3"] + ]; + + Vector x = this.Colour colour_space x; + + // make a colour-ish thing from an image + // back to Colour if we have another 3 band image + // to a vector if bands > 1 + // to a number otherwise + itoc im + = this.Colour nip_type (to_matrix im).value?0, + bands == 3 + = scope.Vector (map mean (bandsplit im)), + bands > 1 + = mean im + { + type = get_header "Type" im; + bands = get_header "Bands" im; + nip_type = Image_type.colour_spaces.lookup 1 0 type; + } + + // methods + oo_binary_table op x = [ + [itoc (op.fn + ((float) (to_image this).value) + ((float) (to_image x).value)), + // here REWRAP means go via image + op.type == Operator_type.COMPOUND_REWRAP] + ] ++ super.oo_binary_table op x; + + oo_unary_table op = [ + [itoc (op.fn ((float) (to_image this).value)), + op.type == Operator_type.COMPOUND_REWRAP] + ] ++ super.oo_unary_table op; +} + +// a subclass with widgets for picking a space and value +Colour_picker default_colour default_value = class + Colour space.item colour.expr { + _vislevel = 3; + + space = Option_enum "Colour space" Image_type.colour_spaces default_colour; + colour = Expression "Colour value" default_value; + + Colour_edit colour_space value = + Colour_picker colour_space value; +} + +/* Base scale type. + */ +Scale caption from to value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string], + [from, "from", check_real], + [to, "to", check_real] + ]; + _check_all = [ + [from < to, "from < to"] + ]; + + Real x = this.Scale caption from to x; + + // methods + oo_binary_table op x = [ + [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) + (op.fn this.value x.value), + is_Scale x && + op.type == Operator_type.ARITHMETIC], + [this.Scale caption (op.fn this.from x) (op.fn this.to x) + (op.fn this.value x), + is_real x && + op.type == Operator_type.ARITHMETIC] + ] ++ super.oo_binary_table op x; +} + +/* Base toggle type. + */ +Toggle caption value = class + scope.Bool value { + _check_args = [ + [caption, "caption", check_string], + [value, "value", check_bool] + ]; + + Bool x = this.Toggle caption x; +} + +/* Base option type. + */ +Option caption labels value = class + scope.Real value { + _check_args = [ + [caption, "caption", check_string], + [labels, "labels", check_string_list], + [value, "value", check_uint] + ]; +} + +/* An option whose value is a string rather than a number. + */ +Option_string caption labels item = class + Option caption labels (index (equal item) labels) { + Option_edit caption labels value + = this.Option_string caption labels (labels?value); +} + +/* Make an option from an enum. + */ +Option_enum caption enum item = class + Option_string caption enum.names item { + // corresponding thing + value_thing = enum.get_thing item; + + Option_edit caption labels value + = this.Option_enum caption enum (enum.names?value); +} + +/* A rectangle. width and height can be -ve. + */ +Rect left top width height = class + _Object { + _check_args = [ + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_real], + [height, "height", check_real] + ]; + + // derived + right = left + width; + bottom = top + height; + + oo_binary_table op x = [ + [equal x, + is_Rect x && + (op.op_name == "equal" || op.op_name == "equal'")], + [!equal x, + is_Rect x && + (op.op_name == "not_equal" || + op.op_name == "not_equal'")], + + // binops with a complex are the same as (comp op comp) + [oo_binary_function op this (Rect (re x) (im x) 0 0), + is_complex x], + + // all others are just pairwise + [this.Rect left' top' width' height', + is_Rect x && + op.type == Operator_type.ARITHMETIC], + [this.Rect left'' top'' width'' height'', + has_number x && + op.type == Operator_type.ARITHMETIC] + ] ++ super.oo_binary_table op x + { + left' = op.fn left x.left; + top' = op.fn top x.top; + width' = op.fn width x.width; + height' = op.fn height x.height; + + left'' = op.fn left x'; + top'' = op.fn top x'; + width'' = op.fn width x'; + height'' = op.fn height x'; + x' = get_number x; + } + + oo_unary_table op = [ + // arithmetic uops just map + [this.Rect left' top' width' height', + op.type == Operator_type.ARITHMETIC], + + // compound uops are just like ops on complex + // do (width, height) so thing like abs(Arrow) work as you'd expect + [op.fn (width, height), + op.type == Operator_type.COMPOUND] + ] ++ super.oo_unary_table op + { + left' = op.fn left; + top' = op.fn top; + width' = op.fn width; + height' = op.fn height; + } + + // empty? ie. contains no pixels + is_empty = width == 0 || height == 0; + + // normalised version, ie. make width/height +ve and flip the origin + nleft + = left + width, width < 0 + = left; + ntop + = top + height, height < 0 + = top; + nwidth = abs width; + nheight = abs height; + nright = nleft + nwidth; + nbottom = ntop + nheight; + + equal x = left == x.left && top == x.top && + width == x.width && height == x.height; + + // contains a point? + includes_point x y + = nleft <= x && x <= nright && ntop <= y && y <= nbottom; + + // contains a rect? just test top left and bottom right points + includes_rect r + = includes_point r.nleft r.ntop && + includes_point r.nright r.nbottom; + + // bounding box of two rects + // if either is empty, can just return the other + union r + = r, is_empty + = this, r.is_empty + = Rect left' top' width' height' + { + left' = min_pair nleft r.nleft; + top' = min_pair ntop r.ntop; + width' = max_pair nright r.nright - left'; + height' = max_pair nbottom r.nbottom - top'; + } + + // intersection of two rects ... empty rect if no intersection + intersect r + = Rect left' top' width'' height'' + { + left' = max_pair nleft r.nleft; + top' = max_pair ntop r.ntop; + width' = min_pair nright r.nright - left'; + height' = min_pair nbottom r.nbottom - top'; + width'' + = width', width > 0 + = 0; + height'' + = height', height > 0 + = 0; + } + + // expand/collapse by n pixels + margin_adjust n + = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); +} + +/* Values for Compression field in image. + */ +Image_compression = class { + NONE = 0; + NO_COMPRESSION = 0; + TCSF_COMPRESSION = 1; + JPEG_COMPRESSION = 2; + LABPACK_COMPRESSED = 3; + RGB_COMPRESSED = 4; + LUM_COMPRESSED = 5; +} + +/* Values for Coding field in image. + */ +Image_coding = class { + NONE = 0; + NOCODING = 0; + COLQUANT = 1; + LABPACK = 2; + RAD = 6; +} + +/* Values for BandFmt field in image. + */ +Image_format = class { + DPCOMPLEX = 9; + DOUBLE = 8; + COMPLEX = 7; + FLOAT = 6; + INT = 5; + UINT = 4; + SHORT = 3; + USHORT = 2; + CHAR = 1; + UCHAR = 0; + NOTSET = -1; + + maxval fmt + = [ + 255, // UCHAR + 127, // CHAR + 65535, // USHORT + 32767, // SHORT + 4294967295, // UINT + 2147483647, // INT + 255, // FLOAT + 255, // COMPLEX + 255, // DOUBLE + 255 // DPCOMPLEX + ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX + = error (_ "bad value for BandFmt"); +} + +/* A lookup table. + */ +Table value = class + _Object { + _check_args = [ + [value, "value", check_rectangular] + ]; + + /* Extract a column. + */ + column n = map (extract n) value; + + /* present col x: is there an x in column col + */ + present col x = member (column col) x; + + /* Look on column from, return matching item in column to. + */ + lookup from to x + = value?n?to, n >= 0 + = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") + { + n = index (equal x) (column from); + } +} + +/* A two column lookup table with the first column a string and the second a + * thing. Used for representing various enums. Option_enum makes a selector + * from one of these. + */ +Enum value = class + Table value { + _check_args = [ + [value, "value", check_enum] + ] + { + check_enum = [is_enum, _ "is [[char, *]]"]; + is_enum x = + is_rectangular x && + is_listof is_string (map (extract 0) x); + } + + // handy ... all the names and things as lists + names = this.column 0; + things = this.column 1; + + // is a legal name or thing + has_name x = this.present 1 x; + has_thing x = this.present 0 x; + + // map things to strings and back + get_name x = this.lookup 1 0 x; + get_thing x = this.lookup 0 1 x; +} + +/* Type field. + */ +Image_type = class { + MULTIBAND = 0; + B_W = 1; + HISTOGRAM = 10; + XYZ = 12; + LAB = 13; + CMYK = 15; + LABQ = 16; + RGB = 17; + UCS = 18; + LCH = 19; + LABS = 21; + sRGB = 22; + YXY = 23; + FOURIER = 24; + RGB16 = 25; + GREY16 = 26; + ARRAY = 27; + scRGB = 28; + + /* Table to get names <-> numbers. + */ + type_names = Enum [ + $MULTIBAND => MULTIBAND, + $B_W => B_W, + $HISTOGRAM => HISTOGRAM, + $XYZ => XYZ, + $LAB => LAB, + $CMYK => CMYK, + $LABQ => LABQ, + $RGB => RGB, + $UCS => UCS, + $LCH => LCH, + $LABS => LABS, + $sRGB => sRGB, + $YXY => YXY, + $FOURIER => FOURIER, + $RGB16 => RGB16, + $GREY16 => GREY16, + $ARRAY => ARRAY, + $scRGB => scRGB + ]; + + /* Table relating nip's colour space names and VIPS's Type numbers. + * Options are generated from this, so match the order to the order in + * the Colour menu. + */ + colour_spaces = Enum [ + $sRGB => sRGB, + $scRGB => scRGB, + $Lab => LAB, + $LCh => LCH, + $XYZ => XYZ, + $Yxy => YXY, + $UCS => UCS + ]; + + /* A slightly larger table ... the types of colorimetric image we can + * have. Add mono, and the S and Q forms of LAB. + */ + image_colour_spaces = Enum [ + $Mono => B_W, + $sRGB => sRGB, + $scRGB => scRGB, + $RGB16 => RGB16, + $GREY16 => GREY16, + $Lab => LAB, + $LabQ => LABQ, + $LabS => LABS, + $LCh => LCH, + $XYZ => XYZ, + $Yxy => YXY, + $UCS => UCS + ]; +} + +/* Base image type. Simple layer over vips_image. + */ +Image value = class + _Object { + _check_args = [ + [value, "value", check_image] + ]; + + // fields from VIPS header + width = get_width value; + height = get_height value; + bands = get_bands value; + format = get_format value; + bits = get_bits value; + coding = get_coding value; + type = get_type value; + xres = get_header "Xres" value; + yres = get_header "Yres" value; + xoffset = get_header "Xoffset" value; + yoffset = get_header "Yoffset" value; + filename = get_header "filename" value; + + // convenience ... the area our pixels occupy, as a rect + rect = Rect 0 0 width height; + + // operator overloading + // (op Image Vector) done in Vector class + oo_binary_table op x = [ + // handle image ++ constant here + [wrap join_result_image, + (has_real x || is_Vector x) && + (op.op_name == "join" || op.op_name == "join'")], + [wrap ite_result_image, + op.op_name == "if_then_else"], + [wrap (op.fn this.value (get_image x)), + has_image x], + [wrap (op.fn this.value (get_number x)), + has_number x], + // if it's not a class on the RHS, handle here ... just apply and + // rewrap + [wrap (op.fn this.value x), + !is_class x] + // all other cases handled by other classes + ] ++ super.oo_binary_table op x + { + // wrap the result with this + // x can be a non-image, eg. compare "Image v == []" vs. + // "Image v == 12" + wrap x + = x, op.type == Operator_type.COMPOUND || + !is_image x + = this.Image x; + + join_result_image + = value ++ new_stuff, op.op_name == "join" + = new_stuff ++ value + { + new_stuff = image_new width height new_bands + format + coding + Image_type.B_W x xoffset yoffset; + new_bands + = get_bands x, has_bands x + = 1; + } + + [then_part, else_part] = x; + + // get things about our output from inputs in this order + objects = [then_part, else_part, this]; + + // properties of our output image + target_bands = get_member_list has_bands get_bands objects; + target_type = get_member_list has_type get_type objects; + + // if one of then/else is an image, get the target format from that + // otherwise, let the non-image objects set the target + target_format + = get_member_list has_format get_format x, + has_member_list has_format x + = NULL; + + to_image x = to_image_size width height target_bands target_format x; + + [then', else'] = map to_image x; + + ite_result_image = image_set_type target_type + (if value then then' else else'); + } + + // FIXME ... yuk ... don't use operator hints, just always rewrap if + // we have an image result + // forced on us by things like abs: + // abs Vector -> real + // abs Image -> Image + // does not fit well with COMPOUND/whatever scheme + oo_unary_table op = [ + [this.Image result, + is_image result], + [result, + true] + ] ++ super.oo_unary_table op + { + result = op.fn this.value; + } +} + +/* Construct an image from a file. + */ +Image_file filename = class + Image value { + _check_args = [ + [filename, "filename", check_string] + ]; + + value = vips_image filename; +} + +Region image left top width height = class + Image value { + _check_args = [ + [image, "Image", check_Image], + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_preal], + [height, "height", check_preal] + ]; + + // a rect for our coordinates + // region.rect gets the rect for the extracted image + region_rect = Rect left top width height; + + // we need to always succeed ... value is our enclosing image if we're + // out of bounds + value + = extract_area left top width height image.value, + image.rect.includes_rect region_rect + = image.value; +} + +Area image left top width height = class + scope.Region image left top width height { + Region image left top width height + = this.Area image left top width height; +} + +Arrow image left top width height = class + scope.Rect left top width height { + _check_args = [ + [image, "Image", check_Image], + [left, "left", check_real], + [top, "top", check_real], + [width, "width", check_real], + [height, "height", check_real] + ]; + + Rect l t w h = this.Arrow image l t w h; +} + +HGuide image top = class + scope.Arrow image image.rect.left top image.width 0 { + Arrow image left top width height = this.HGuide image top; +} + +VGuide image left = class + scope.Arrow image left image.rect.top 0 image.height { + Arrow image left top width height = this.VGuide image left; +} + +Mark image left top = class + scope.Arrow image left top 0 0 { + Arrow image left top width height = this.Mark image left top; +} + +// convenience functions: ... specify position as [0 .. 1) + +Region_relative image u v w h + = Region image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +Area_relative image u v w h + = Area image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +Arrow_relative image u v w h + = Arrow image + (image.width * u) + (image.height * v) + (image.width * w) + (image.height * h); + +VGuide_relative image v + = VGuide image (image.height * v); + +HGuide_relative image u + = HGuide image (image.width * u); + +Mark_relative image u v + = Mark image + (image.width * u) + (image.height * v); + +Kernel_type = class { + NEAREST_NEIGHBOUR = 0; + LINEAR = 1; + CUBIC = 2; + LANCZOS2 = 3; + LANCZOS3 = 4; + + // Should introspect to get the list of interpolators :-( + // We can "dir" on VipsInterpolate to get a list of them, but we + // can't get i18n'd descriptions until we have more + // introspection stuff in nip2. + + /* Table to map kernel numbers to descriptive strings + */ + descriptions = [ + _ "Nearest neighbour", + _ "Linear", + _ "Cubic", + _ "Lanczos, two lobes", + _ "Lanczos, three lobes" + ]; + + /* And to vips enum nicknames. + */ + types = [ + "nearest", + "linear", + "cubic", + "lanczos2", + "lanczos3" + ]; +} + +Kernel type = class { + value = Kernel_type.types?type; +} + +Kernel_linear = Kernel Kernel_type.LINEAR; + +Kernel_picker default = class + Kernel kernel.value { + _vislevel = 2; + + kernel = Option "Kernel" Kernel_type.descriptions default; +} + +Interpolate_type = class { + NEAREST_NEIGHBOUR = 0; + BILINEAR = 1; + BICUBIC = 2; + LBB = 3; + NOHALO = 4; + VSQBS = 5; + + // Should introspect to get the list of interpolators :-( + // We can "dir" on VipsInterpolate to get a list of them, but we + // can't get i18n'd descriptions until we have more + // introspection stuff in nip2. + + /* Table to map interpol numbers to descriptive strings + */ + descriptions = [ + _ "Nearest neighbour", + _ "Bilinear", + _ "Bicubic", + _ "Upsize: reduced halo bicubic (LBB)", + _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", + _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" + ]; + + /* And to vips type names. + */ + types = [ + "VipsInterpolateNearest", + "VipsInterpolateBilinear", + "VipsInterpolateBicubic", + "VipsInterpolateLbb", + "VipsInterpolateNohalo", + "VipsInterpolateVsqbs" + ]; +} + +Interpolate type options = class { + value = vips_object_new Interpolate_type.types?type [] options; +} + +Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; + +Interpolate_picker default = class + Interpolate interp.value [] { + _vislevel = 2; + + interp = Option "Interpolation" Interpolate_type.descriptions default; +} + +Render_intent = class { + PERCEPTUAL = 0; + RELATIVE = 1; + SATURATION = 2; + ABSOLUTE = 3; + + /* Table to get names <-> numbers. + */ + names = Enum [ + _ "Perceptual" => PERCEPTUAL, + _ "Relative" => RELATIVE, + _ "Saturation" => SATURATION, + _ "Absolute" => ABSOLUTE + ]; +} + +// abstract base class for toolkit menus +Menu = class {} + +// a "----" line in a menu +Menuseparator = class Menu {} + +// abstract base class for items in menus +Menuitem label tooltip = class Menu {} + +Menupullright label tooltip = class Menuitem label tooltip {} + +Menuaction label tooltip = class Menuitem label tooltip {} + +/* Plots. + */ + +Plot_style = class { + POINT = 0; + LINE = 1; + SPLINE = 2; + BAR = 3; + + names = Enum [ + _ "Point" => POINT, + _ "Line" => LINE, + _ "Spline" => SPLINE, + _ "Bar" => BAR + ]; +} + +Plot_format = class { + YYYY = 0; + XYYY = 1; + XYXY = 2; + + names = Enum [ + _ "YYYY" => YYYY, + _ "XYYY" => XYXY, + _ "XYXY" => XYXY + ]; +} + +Plot_type = class { + /* Lots of Ys (ie. multiple line plots). + */ + YYYY = 0; + + /* First column of matrix is X position, others are Ys (ie. multiple XY + * line plots, all with the same Xes). + */ + XYYY = 1; + + /* Many independent XY plots. + */ + XYXY = 2; +} + +/* "options" is a list of ["key", value] pairs. + */ +Plot options value = class + scope.Image value { + Image value = this.Plot options value; + to_image dpi = extract_bands 0 3 + (graph_export_image (to_real dpi) this); +} + +Plot_matrix options value = class + Plot options (to_image value).value { +} + +Plot_histogram value = class + scope.Plot [] value { +} + +Plot_xy value = class + scope.Plot [$format => Plot_format.XYYY] value { +} + +/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate + * empty slots, for example. + */ +NULL = class + _Object { + oo_binary_table op x = [ + // the only operation we allow is equality .. use pointer equality, + // this lets us test a == NULL and a != NULL + [this === x, + op.type == Operator_type.RELATIONAL && + op.op_name == "equal"], + [this !== x, + op.type == Operator_type.RELATIONAL && + op.op_name == "not_equal"] + ] ++ super.oo_binary_table op x; +} + +Blend_type = class { + CLEAR = 0; + SOURCE = 1; + OVER = 2; + IN = 3; + OUT = 4; + ATOP = 5; + DEST = 6; + DEST_OVER = 7; + DEST_IN = 8; + DEST_OUT = 9; + DEST_ATOP = 10; + XOR = 11; + ADD = 12; + SATURATE = 13; + MULTIPLY = 14; + SCREEN = 15; + OVERLAY = 16; + DARKEN = 17; + LIGHTEN = 18; + COLOUR_DODGE = 19; + COLOUR_BURN = 20; + HARD_LIGHT = 21; + SOFT_LIGHT = 22; + DIFFERENCE = 23; + EXCLUSION = 24; + + /* Table to map blend numbers to descriptive strings + */ + descriptions = [ + _ "Clear", + _ "Source", + _ "Over", + _ "In", + _ "Out", + _ "Atop", + _ "Dest", + _ "Dest over", + _ "Dest in", + _ "Dest out", + _ "Dest atop", + _ "Xor", + _ "Add", + _ "Saturate", + _ "Multiply", + _ "Screen", + _ "Overlay", + _ "Darken", + _ "Lighten", + _ "Colour dodge", + _ "Colour burn", + _ "Hard light", + _ "Soft light", + _ "Difference", + _ "Exclusion" + ]; + + /* And to vips enum nicknames. + */ + types = Enum [ + $clear => "clear", + $source => "source", + $over => "over", + $in => "in", + $out => "out", + $atop => "atop", + $dest => "dest", + $dest_over => "dest_over", + $dest_in => "dest_in", + $dest_out => "dest_out", + $dest_atop => "dest_atop", + $xor => "xor", + $add => "add", + $saturate => "saturate", + $multiply => "multiply", + $screen => "screen", + $overlay => "overlay", + $darken => "darken", + $lighten => "lighten", + $colour_dodge => "colour_dodge", + $colour_burn => "colour_burn", + $hard_light => "hard_light", + $soft_light => "soft_light", + $difference => "difference", + $exclusion => "exclusion" + ]; +} + +Blend type = class { + value = Blend_type.types?type; +} + +Blend_over = Blend Blend_type.OVER; + +Blend_picker default = class + Blend blend.value { + _vislevel = 2; + + blend = Option "Blend" Blend_type.descriptions default; +} + +Combine_type = class { + MAX = 0; + SUM = 1; + MIN = 2; + + enum = Enum [ + _ "Maximum" => MAX, + _ "Sum" => SUM, + _ "Minimum" => MIN + ]; +} + +Combine type = class { + value = Combine_type.enum.names?type; +} + +Combine_sum = Combine Combine_type.SUM; + +Combine_picker default = Option "Combine" Combine_type.enum.names default; diff --git a/share/nip2/compat/Makefile.am b/share/nip2/compat/Makefile.am index 3fe0b923..3705b1c9 100644 --- a/share/nip2/compat/Makefile.am +++ b/share/nip2/compat/Makefile.am @@ -1 +1,2 @@ -SUBDIRS = 7.8 7.9 7.10 7.12 7.14 7.16 7.24 7.26 7.28 7.38 7.40 8.2 8.3 8.4 8.5 +SUBDIRS = 7.8 7.9 7.10 7.12 7.14 7.16 7.24 7.26 7.28 7.38 7.40 8.2 8.3 \ + 8.4 8.5 8.6 diff --git a/share/nip2/start/Preferences.ws b/share/nip2/start/Preferences.ws index 522c8714..8577a27c 100644 --- a/share/nip2/start/Preferences.ws +++ b/share/nip2/start/Preferences.ws @@ -1,5 +1,5 @@ - + diff --git a/share/nip2/start/_types.def b/share/nip2/start/_types.def index d07712f6..c657895f 100644 --- a/share/nip2/start/_types.def +++ b/share/nip2/start/_types.def @@ -1086,8 +1086,9 @@ Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; - LANCZOS2 = 3; - LANCZOS3 = 4; + MITCHELL = 3; + LANCZOS2 = 4; + LANCZOS3 = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we @@ -1100,6 +1101,7 @@ Kernel_type = class { _ "Nearest neighbour", _ "Linear", _ "Cubic", + _ "Mitchell", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; @@ -1110,6 +1112,7 @@ Kernel_type = class { "nearest", "linear", "cubic", + "mitchell", "lanczos2", "lanczos3" ]; From ed7ce706ec710b41fa464121cf8836a471ed8ed2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 4 Oct 2018 15:42:38 +0100 Subject: [PATCH 15/37] update links for new home --- README.md | 4 ++-- nip2.appdata.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ce2ec3d1..b121a610 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ Status](https://build.snapcraft.io/badge/jcupitt/nip2.svg)](https://build.snapcraft.io/user/jcupitt/nip2) nip2 is a GUI for the [VIPS image processing -library](https://jcupitt.github.io/libvips). It's a little like a spreadsheet: +library](https://libvips.github.io/libvips). It's a little like a spreadsheet: you create a set of formula connecting your objects together, and on a change nip2 recalculates. You can probably install nip2 via your package manager. For Windows and OS X, you can download a binary from the [nip2 releases -area](https://github.com/jcupitt/nip2/releases). Only read on if you want to +area](https://github.com/libvips/nip2/releases). Only read on if you want to compile yourself from source. # Building nip2 from source diff --git a/nip2.appdata.xml b/nip2.appdata.xml index 6d112d4f..c5025b9d 100644 --- a/nip2.appdata.xml +++ b/nip2.appdata.xml @@ -22,8 +22,8 @@

- https://raw.githubusercontent.com/jcupitt/nip2/master/doc/src/figs/snap1.jpg + https://raw.githubusercontent.com/libvips/nip2/master/doc/src/figs/snap1.jpg - https://jcupitt.github.io/libvips - https://github.com/jcupitt/nip2 + https://libvips.github.io/libvips + https://github.com/libvips/nip2 From f0af18ec24418589c84df15747f7d5faca866588 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 13 Nov 2018 17:44:38 +0000 Subject: [PATCH 16/37] fix uint status bar would display -ve values for pixels > 2**31 see https://github.com/libvips/nip2/issues/74 --- ChangeLog | 3 +++ configure.ac | 4 ++-- src/statusview.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index cf0964b8..5584bb47 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.7.1 13/11/18 +- fix uint status bar pixels >2**31 [Rob Erdmann] + started 8.7.0 22/5/18 - added vips7compat.h include for libvips 8.7 - more output for -V to help debugging CLI mode diff --git a/configure.ac b/configure.ac index 306a634a..15ada319 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.7.0], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.7.1], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -17,7 +17,7 @@ dnl m4_define([nip_major_version], [8]) m4_define([nip_minor_version], [7]) -m4_define([nip_micro_version], [0]) +m4_define([nip_micro_version], [1]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/src/statusview.c b/src/statusview.c index 5458d0b7..28e01554 100644 --- a/src/statusview.c +++ b/src/statusview.c @@ -402,7 +402,7 @@ statusview_mouse_band( StatusviewBand *svb, void *e ) break; case IM_BANDFMT_UINT: - set_glabel( svb->val, "%d", + set_glabel( svb->val, "%u", ((unsigned int *)e)[svb->bandno] ); break; From 1b204c9d965592e733e9d4718cd6c9869511fc95 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 5 Dec 2018 17:31:42 +0000 Subject: [PATCH 17/37] add note about docs to README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b121a610..789579f1 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,11 @@ Windows and OS X, you can download a binary from the [nip2 releases area](https://github.com/libvips/nip2/releases). Only read on if you want to compile yourself from source. +# Documentation + +nip2 comes with a 50-page manual --- press F1 or Help / Contents in the +program to view it. + # Building nip2 from source In the nip2 directory you should just be able to do: From 3caba997b63b9514ae89b242e144ee75d0659494 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 7 Jan 2019 08:56:56 +0000 Subject: [PATCH 18/37] try adjusting marshal types icontainer and watch were declaring some signals as taking objevct arguments, but using a simple pointer marshal function. This is harmless on Debian-dervived distros, but redhat seem to build glib with run-time time checks which spot this and NULL out pointers, causing a crash. See https://github.com/libvips/nip2/issues/75 --- src/icontainer.c | 6 +++--- src/watch.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/icontainer.c b/src/icontainer.c index 1180b96a..5e68cef9 100644 --- a/src/icontainer.c +++ b/src/icontainer.c @@ -795,7 +795,7 @@ icontainer_class_init( iContainerClass *class ) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, child_remove ), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); @@ -804,7 +804,7 @@ icontainer_class_init( iContainerClass *class ) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, current ), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); @@ -813,7 +813,7 @@ icontainer_class_init( iContainerClass *class ) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, child_detach ), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); diff --git a/src/watch.c b/src/watch.c index e16c0b0d..f3f372df 100644 --- a/src/watch.c +++ b/src/watch.c @@ -61,9 +61,9 @@ watchgroup_class_init( WatchgroupClass *class ) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( WatchgroupClass, watch_changed ), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - G_TYPE_POINTER ); + TYPE_WATCH ); } static void From d81f3c8ffef1c191d50855218a179ea784403766 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 15 Feb 2019 18:04:53 +0000 Subject: [PATCH 19/37] test conv test --- test/workspaces/test_conv.ws | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/test/workspaces/test_conv.ws b/test/workspaces/test_conv.ws index c01bbe7f..29e6f5cc 100644 --- a/test/workspaces/test_conv.ws +++ b/test/workspaces/test_conv.ws @@ -1,7 +1,7 @@ - - - + + + @@ -96,7 +96,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -230,7 +230,7 @@ - + @@ -289,7 +289,7 @@ - + @@ -305,7 +305,7 @@ - + @@ -356,7 +356,7 @@ - + @@ -407,7 +407,7 @@ - + @@ -485,7 +485,7 @@ - + @@ -556,9 +556,14 @@ + + + + + - + From d3b959bad4718dbfd325793edf58b8b5ae179f9f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 17 Dec 2019 08:22:09 +0000 Subject: [PATCH 20/37] small doc update See https://github.com/libvips/nip2/issues/84 Thanks Matt --- ChangeLog | 1 + doc/src/program.tex | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5584bb47..06015e81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ started 8.7.1 13/11/18 - fix uint status bar pixels >2**31 [Rob Erdmann] +- fix crash on redhat [bgilbert] started 8.7.0 22/5/18 - added vips7compat.h include for libvips 8.7 diff --git a/doc/src/program.tex b/doc/src/program.tex index 3ff4a811..5fa53827 100644 --- a/doc/src/program.tex +++ b/doc/src/program.tex @@ -821,7 +821,8 @@ \section{Lists and recursion} \ct{map3 fn l1 l2 l3} & map 3-ary function \ct{fn} over lists \ct{l1}, \ct{l2} and \ct{l3} \\ \ct{member l x} & true if \ct{x} is a member of list \ct{l} \\ -\ct{mkset l} & remove duplicates from list \ct{l} \\ +\ct{mkset eq l} & remove duplicates from list \ct{l} with equality + function \ct{eq} \\ \ct{postfix l r} & add element \ct{r} to the end of list \ct{l} \\ \ct{product l} & product of list l \\ \ct{repeat x} & make an infinite list of \ct{x}es \\ @@ -1510,7 +1511,7 @@ \subsection{Workspaces} \subsection{The \ct{Image} class} \mylabel{sec:Image}. -say supports mioxed ops with real, vector and complex constants +say supports mixed ops with real, vector and complex constants \subsection{The \ct{Colour} class} \mylabel{sec:colour} From 92d023706f8f1356ee080cc9f862b5be6d06e733 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 4 Mar 2020 16:33:56 +0000 Subject: [PATCH 21/37] revise README --- Makefile.am | 1 + README.md | 76 ++++++++++++++----------------------------------- screenshot.png | Bin 0 -> 233031 bytes 3 files changed, 23 insertions(+), 54 deletions(-) create mode 100644 screenshot.png diff --git a/Makefile.am b/Makefile.am index 5b5b4bbc..4c8aa931 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = \ doc \ proj \ m4 \ + screenshot.png \ nip2.desktop.in \ nip2.xml \ nip2.appdata.xml diff --git a/README.md b/README.md index 789579f1..4c13b943 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,29 @@ library](https://libvips.github.io/libvips). It's a little like a spreadsheet: you create a set of formula connecting your objects together, and on a change nip2 recalculates. +[![Screenshot](screenshot.png)](screenshot.png) + +## Installing + You can probably install nip2 via your package manager. For Windows and OS X, you can download a binary from the [nip2 releases -area](https://github.com/libvips/nip2/releases). Only read on if you want to -compile yourself from source. +area](https://github.com/libvips/nip2/releases). If you must build from +source, see the section below. -# Documentation +## Documentation nip2 comes with a 50-page manual --- press F1 or Help / Contents in the program to view it. -# Building nip2 from source +## Building nip2 from source -In the nip2 directory you should just be able to do: +In the nip2 directory you should just be able to do the usual: - $ ./configure - $ make - $ sudo make install +``` +./configure +make +sudo make install +``` By default this will install files to `/usr/local`. @@ -33,60 +39,22 @@ nip2 needs in order to be able to build. If you downloaded from GIT you'll need: - $ ./autogen.sh +``` +./autogen.sh +``` first to build the configure system. -# Dependencies - nip2 needs vips, gtk2 and libxml2 at runtime and flex/bison at compile time. -If you have fftw3, gsl, goffice, libgvc you get extra optional, but useful, -features. - -# Tips - -production build with - - ./configure --prefix=/home/john/vips - -debug build with - - CFLAGS="-g -Wall" ./configure --prefix=/home/john/vips - -(--enable-debug turns on and off automatically with development / production -minor version numbers) - -leak check - - export G_DEBUG=gc-friendly - export G_SLICE=always-malloc - valgrind --suppressions=/home/john/nip2.supp \ - --leak-check=yes \ - nip2 ... > nip2-vg.log 2>&1 - -memory access check - - valgrind --suppressions=/home/john/nip2.supp \ - --leak-check=no --db-attach=yes \ - nip2 ... > nip2-vg.log 2>&1 - -or put "--suppressions=/home/john/nip2.supp" into ~/.valgrindrc - -profiling - - valgrind --tool=callgrind \ - --suppressions=/home/john/nip2.supp \ - nip2 ... > nip2-vg.log 2>&1 - -Disclaimer: No guarantees of performance accompany this software, nor is any -responsibility assumed on the part of the authors. Please read the licence -agreement. +If you have fftw3, gsl, goffice, libgvc you get extra features. -# snapcraft +### snapcraft Rebuild snap with: - snapcraft cleanbuild +``` +snapcraft cleanbuild +``` Though it's done automatically on a push. diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..224dfebef7f68160767c11ac124abec6c6329660 GIT binary patch literal 233031 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%0A^k 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

~@x)7n&}$`89TH=4Y>!kV-hw`97ORzdik%f&6`O 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

~@x)7n&}$`89TH=4Y>!kV-hw`97ORzdik%f&6`O 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

MH<+qf0$w!q#zi_mR4ObxT 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 z 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 !L3$*&Z+}YQkDfF! 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 z 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 {;41_yC1`gk2v0>T#`1mY%@ z0DZO^X{?RG;^b<#vl`*o5y}`=Fyw#^w}1h;NB Date: Sun, 2 Feb 2025 16:09:16 -0700 Subject: [PATCH 35/37] Do not redeclare statfs() --- src/ip.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ip.h b/src/ip.h index 7a7c3d91..6cfbbd46 100644 --- a/src/ip.h +++ b/src/ip.h @@ -108,7 +108,6 @@ #endif /*HAVE_SYS_STATVFS_H*/ #ifdef HAVE_SYS_VFS_H #include -extern int statfs(); #endif /*HAVE_SYS_VFS_H*/ #ifdef HAVE_SYS_MOUNT_H #include From 8c60c517b59f806da84d57cb1d083a213b811151 Mon Sep 17 00:00:00 2001 From: Orion Poplawski Date: Sun, 2 Feb 2025 16:12:38 -0700 Subject: [PATCH 36/37] Declare function arguments for function pointer --- src/plot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot.c b/src/plot.c index 52b7b9cd..fb310939 100644 --- a/src/plot.c +++ b/src/plot.c @@ -375,7 +375,7 @@ plot_class_get( Classmodel *classmodel, PElement *root ) Imageinfo *ii2; IMAGE *t; DOUBLEMASK *mask; - int (*fn)(); + int (*fn)(VipsImage *, VipsImage *); /* nx1 or 1xm images only ... use Bands for columns. */ From d5743eefba66a5ceb9ff1b0d04f4ff04c1268406 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 25 Feb 2025 18:56:29 +0000 Subject: [PATCH 37/37] note nip4 on readme --- README.md | 9 +++++++-- src/dummy.c | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/dummy.c diff --git a/README.md b/README.md index 7eef5c12..75f9664e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ # nip2 --- a user interface for libvips -[![Snap -Status](https://build.snapcraft.io/badge/jcupitt/nip2.svg)](https://build.snapcraft.io/user/jcupitt/nip2) +We now have a first public test release of nip4, a rewrite of nip2 for the +gtk4 UI toolkit. If you have some time to try it, any feedback would +be very welcome: + +https://github.com/jcupitt/nip4/releases + +## nip2 nip2 is a GUI for the [libvips image processing library](https://libvips.github.io/libvips). It's a little like a spreadsheet: diff --git a/src/dummy.c b/src/dummy.c new file mode 100644 index 00000000..679b26f3 --- /dev/null +++ b/src/dummy.c @@ -0,0 +1 @@ +int poop () {} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy

MH<+qf0$w!q#zi_mR4ObxT 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 z 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 !L3$*&Z+}YQkDfF! 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 z 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 {;41_yC1`gk2v0>T#`1mY%@ z0DZO^X{?RG;^b<#vl`*o5y}`=Fyw#^w}1h;NB Date: Wed, 4 Mar 2020 16:36:33 +0000 Subject: [PATCH 22/37] more README polish --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4c13b943..254bbb58 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Snap Status](https://build.snapcraft.io/badge/jcupitt/nip2.svg)](https://build.snapcraft.io/user/jcupitt/nip2) -nip2 is a GUI for the [VIPS image processing +nip2 is a GUI for the [libvips image processing library](https://libvips.github.io/libvips). It's a little like a spreadsheet: you create a set of formula connecting your objects together, and on a change nip2 recalculates. @@ -14,7 +14,7 @@ nip2 recalculates. You can probably install nip2 via your package manager. For Windows and OS X, you can download a binary from the [nip2 releases -area](https://github.com/libvips/nip2/releases). If you must build from +page](https://github.com/libvips/nip2/releases). If you have to build from source, see the section below. ## Documentation @@ -32,10 +32,8 @@ make sudo make install ``` -By default this will install files to `/usr/local`. - -See the Dependencies section below for a list of the things that -nip2 needs in order to be able to build. +By default this will install files to `/usr/local`. Check the summary at the +end of `configure` and make sure you have all of the features you want. If you downloaded from GIT you'll need: @@ -46,8 +44,7 @@ If you downloaded from GIT you'll need: first to build the configure system. nip2 needs vips, gtk2 and libxml2 at runtime and flex/bison at compile time. - -If you have fftw3, gsl, goffice, libgvc you get extra features. +If you have fftw3, gsl, goffice, libgvc you get extra features. ### snapcraft From b90bf51807dbe36d945cea5cca98e2d8416f94cb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 4 Mar 2020 16:37:10 +0000 Subject: [PATCH 23/37] README again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 254bbb58..f8348a47 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# nip2 - a user interface for the VIPS image processing library +# nip2 --- a user interface for libvips [![Snap Status](https://build.snapcraft.io/badge/jcupitt/nip2.svg)](https://build.snapcraft.io/user/jcupitt/nip2) From eb5b94ea419824d1b5a40aa0c416bcbd26a1ac3d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 3 Apr 2020 13:00:16 +0100 Subject: [PATCH 24/37] allow larger arrays as params to vips_call etc. --- src/heap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/heap.c b/src/heap.c index 919f90a6..5e9e9787 100644 --- a/src/heap.c +++ b/src/heap.c @@ -1895,12 +1895,13 @@ heap_ip_to_gvalue( PElement *in, GValue *out ) */ else if( heap_is_imagevec( in, &result ) && result ) { - Imageinfo *iivec[100]; + Imageinfo *iivec[MAX_VEC]; VipsImage **ivec; int n; int i; - if( (n = heap_get_imagevec( in, iivec, 100 )) < 0 ) + if( (n = heap_get_imagevec( in, + iivec, MAX_VEC )) < 0 ) return( FALSE ); g_value_init( out, VIPS_TYPE_ARRAY_IMAGE ); vips_value_set_array_image( out, n ); @@ -1916,10 +1917,11 @@ heap_ip_to_gvalue( PElement *in, GValue *out ) } else if( heap_is_realvec( in, &result ) && result ) { - double realvec[100]; + double realvec[MAX_VEC]; int n; - if( (n = heap_get_realvec( in, realvec, 100 )) < 0 ) + if( (n = heap_get_realvec( in, + realvec, MAX_VEC )) < 0 ) return( FALSE ); g_value_init( out, VIPS_TYPE_ARRAY_DOUBLE ); vips_value_set_array_double( out, realvec, n ); From c924df0779de64a2cf030df87d00c5e171296719 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 10 Apr 2020 13:52:46 +0100 Subject: [PATCH 25/37] add Trim menu item --- ChangeLog | 3 +++ configure.ac | 6 +++--- share/nip2/start/Image.def | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 06015e81..d7c3a0d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.9.0 10/4/20 +- add find_trim + started 8.7.1 13/11/18 - fix uint status bar pixels >2**31 [Rob Erdmann] - fix crash on redhat [bgilbert] diff --git a/configure.ac b/configure.ac index 15ada319..02e05201 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.7.1], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.9.0], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -16,8 +16,8 @@ dnl of them. dnl m4_define([nip_major_version], [8]) -m4_define([nip_minor_version], [7]) -m4_define([nip_micro_version], [1]) +m4_define([nip_minor_version], [9]) +m4_define([nip_micro_version], [0]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/share/nip2/start/Image.def b/share/nip2/start/Image.def index 962dbd18..4c0deb53 100644 --- a/share/nip2/start/Image.def +++ b/share/nip2/start/Image.def @@ -1286,6 +1286,29 @@ Image_crop_item = class } } +Trim_item = class Menuaction "_Trim" "crop away edges" { + action x = class + _result { + _vislevel = 3; + + thresh = Scale "threshold" 0 100 10; + background = Expression "Background" default_background + { + default_background + = map mean (bandsplit (extract_area 0 0 1 1 x)); + } + + _result + = Region x l t w h + { + [l, t, w, h] = vips_call "find_trim" [x.value] [ + $threshold => thresh.value, + $background => background.expr + ]; + } + } +} + Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b From 3d35a5610c2604ef8759b5ce8b4a326ff8f7c0fb Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Sat, 20 Feb 2021 00:08:25 -0800 Subject: [PATCH 26/37] Fix compilation with ICU 68 nip2 depends on the ICU library via libxml2. ICU released version 68 on 2010-10-28, and switched to using the C99 bool type in its public headers. nip2 was using "bool" as the name for a gboolean in the ParseConst struct. This commit renames this to "boolean". --- src/compile.c | 4 ++-- src/lex.l | 4 ++-- src/tree.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compile.c b/src/compile.c index bd9c6ae3..2d341db2 100644 --- a/src/compile.c +++ b/src/compile.c @@ -815,7 +815,7 @@ compile_graph( Compile *compile, ParseNode *pn, PElement *out ) break; case PARSE_CONST_BOOL: - PEPUTP( out, ELEMENT_BOOL, pn->con.val.bool ); + PEPUTP( out, ELEMENT_BOOL, pn->con.val.boolean ); break; case PARSE_CONST_ELIST: @@ -2523,7 +2523,7 @@ compile_pattern_condition( Compile *compile, int i; n.type = PARSE_CONST_BOOL; - n.val.bool = TRUE; + n.val.boolean = TRUE; node = tree_const_new( compile, n ); for( i = depth - 1; i >= 0; i-- ) { diff --git a/src/lex.l b/src/lex.l index db5d9935..de8a152f 100644 --- a/src/lex.l +++ b/src/lex.l @@ -207,7 +207,7 @@ TRUE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; - yylval.yy_const.val.bool = TRUE; + yylval.yy_const.val.boolean = TRUE; return( TK_CONST ); } @@ -216,7 +216,7 @@ FALSE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; - yylval.yy_const.val.bool = FALSE; + yylval.yy_const.val.boolean = FALSE; return( TK_CONST ); } diff --git a/src/tree.h b/src/tree.h index 2b18ef2a..9238a990 100644 --- a/src/tree.h +++ b/src/tree.h @@ -126,7 +126,7 @@ struct _ParseConst { union { double num; char *str; - gboolean bool; + gboolean boolean; int ch; } val; }; From 1d6233b9f6ac3a398573b6b38c7304c99fe233e2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 15 Feb 2023 10:06:21 +0000 Subject: [PATCH 27/37] fix build with c99 see https://github.com/libvips/nip2/issues/104 --- ChangeLog | 3 +++ configure.ac | 4 ++-- src/compile.c | 4 ++-- src/lex.l | 4 ++-- src/tree.h | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index d7c3a0d9..5cdabb36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.9.1 15/2/23 +- fix build with --std=c99 [Schamschula] + started 8.9.0 10/4/20 - add find_trim diff --git a/configure.ac b/configure.ac index 02e05201..e3b412c9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.9.0], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.9.1], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -17,7 +17,7 @@ dnl m4_define([nip_major_version], [8]) m4_define([nip_minor_version], [9]) -m4_define([nip_micro_version], [0]) +m4_define([nip_micro_version], [1]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/src/compile.c b/src/compile.c index 2d341db2..ecf40b85 100644 --- a/src/compile.c +++ b/src/compile.c @@ -815,7 +815,7 @@ compile_graph( Compile *compile, ParseNode *pn, PElement *out ) break; case PARSE_CONST_BOOL: - PEPUTP( out, ELEMENT_BOOL, pn->con.val.boolean ); + PEPUTP( out, ELEMENT_BOOL, pn->con.val.bol ); break; case PARSE_CONST_ELIST: @@ -2523,7 +2523,7 @@ compile_pattern_condition( Compile *compile, int i; n.type = PARSE_CONST_BOOL; - n.val.boolean = TRUE; + n.val.bol = TRUE; node = tree_const_new( compile, n ); for( i = depth - 1; i >= 0; i-- ) { diff --git a/src/lex.l b/src/lex.l index de8a152f..9cfc2e93 100644 --- a/src/lex.l +++ b/src/lex.l @@ -207,7 +207,7 @@ TRUE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; - yylval.yy_const.val.boolean = TRUE; + yylval.yy_const.val.bol = TRUE; return( TK_CONST ); } @@ -216,7 +216,7 @@ FALSE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; - yylval.yy_const.val.boolean = FALSE; + yylval.yy_const.val.bol = FALSE; return( TK_CONST ); } diff --git a/src/tree.h b/src/tree.h index 9238a990..8a764bbb 100644 --- a/src/tree.h +++ b/src/tree.h @@ -126,7 +126,7 @@ struct _ParseConst { union { double num; char *str; - gboolean boolean; + gboolean bol; int ch; } val; }; From 2f819a37a47bbab9f09c161c9c0fade57cea0766 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 16 Feb 2023 14:55:38 +0000 Subject: [PATCH 28/37] update copyright date --- src/ip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ip.h b/src/ip.h index fcb06b56..7a7c3d91 100644 --- a/src/ip.h +++ b/src/ip.h @@ -202,7 +202,7 @@ extern int statfs(); #define IP_NAME PACKAGE "-" VERSION #define MAX_LINELENGTH (120) /* Max chars we display of value */ #define MAX_RECENT (10) /* Number of recent items in file menu */ -#define NIP_COPYRIGHT "%s: ©2018 Imperial College, London" +#define NIP_COPYRIGHT "%s: ©2023 libvips.org" /* Our stock_ids. */ From 97cd35c40929134e0bfa9d04d796a29235b1898e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 16 Feb 2023 15:07:41 +0000 Subject: [PATCH 29/37] expand readme a bit --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f8348a47..7eef5c12 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,15 @@ Status](https://build.snapcraft.io/badge/jcupitt/nip2.svg)](https://build.snapcr nip2 is a GUI for the [libvips image processing library](https://libvips.github.io/libvips). It's a little like a spreadsheet: you create a set of formula connecting your objects together, and on a change -nip2 recalculates. +nip2 will recalculate. This makes it convenient for developing image processing +systems since you can watch pixels change as you adjust your equations. + +Because nip2 uses libvips as the image processing engine it can handle very +large images and only needs a little memory. It scales to fairly complex +workflows: I've used it to develop systems with more than 10,000 cells, +analyzing images of many tens of gigabytes. It has a batch mode, so you +can run any image processing system you develop from the command-line and +without a GUI. [![Screenshot](screenshot.png)](screenshot.png) From 451b25575130b17c35791818c947c690bdbfd5e9 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 22 Nov 2023 17:55:35 +0000 Subject: [PATCH 30/37] fix a lockup The parse would lockup in REPL mode on comment to end of line. See https://github.com/libvips/nip2/issues/111 Thanks MvGulik --- ChangeLog | 3 +++ configure.ac | 4 ++-- src/lex.l | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5cdabb36..87c5bebd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +started 8.9.2 22/11/23 +- fix a lockup with comment characters in REPL [MvGulik] + started 8.9.1 15/2/23 - fix build with --std=c99 [Schamschula] diff --git a/configure.ac b/configure.ac index e3b412c9..ac6a03b4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([nip2], [8.9.1], [vipsip@jiscmail.ac.uk]) +AC_INIT([nip2], [8.9.2], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) @@ -17,7 +17,7 @@ dnl m4_define([nip_major_version], [8]) m4_define([nip_minor_version], [9]) -m4_define([nip_micro_version], [1]) +m4_define([nip_micro_version], [2]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) diff --git a/src/lex.l b/src/lex.l index 9cfc2e93..b960b9b7 100644 --- a/src/lex.l +++ b/src/lex.l @@ -180,7 +180,7 @@ read_char( void ) /* Read string up to \n, EOF. */ - while( (ch = input()) != EOF && ch != '\n' ) + while( (ch = input()) != EOF && ch != '\n' && ch != '\0') ; } From fdca6b79dd3690e5d991c16c5e77a3b072363428 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 31 Jan 2024 15:08:55 +0000 Subject: [PATCH 31/37] remove old snapcraft file it no longer worked --- .snapcraft.yaml | 83 ------------------------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 .snapcraft.yaml diff --git a/.snapcraft.yaml b/.snapcraft.yaml deleted file mode 100644 index dcf3af94..00000000 --- a/.snapcraft.yaml +++ /dev/null @@ -1,83 +0,0 @@ -name: vips-nip2 -icon: share/nip2/data/vips-128.png -version: 'master' -summary: An image-processing spreadsheet. -description: | - nip2 is half-way between a spreadsheet and a paint program. - It's terrible for retouching photographs, but very handy for automating image - processing tasks. - - It can process multi-gigabyte images efficiently. It has good support for - scientific image formats. It's much faster and more flexible than tools like - ImageJ. - - This snap is git master nip2 built with git master libvips. - -grade: devel -confinement: strict - -apps: - nip2: - command: desktop-launch nip2 - desktop: share/applications/nip2.desktop - plugs: [x11, home, gsettings] - -parts: - vips-part: - source: https://github.com/jcupitt/libvips - source-branch: master - source-type: git - plugin: autotools - build-packages: - - gtk-doc-tools - - gobject-introspection - - intltool - - libgirepository1.0-dev - - libglib2.0-dev - - libfftw3-dev - - libexpat1-dev - - libxml2-dev - - libjpeg-turbo8-dev - - libtiff5-dev - - libopenslide-dev - - libopenexr-dev - - libcfitsio-dev - - libmatio-dev - - libgif-dev - - libpoppler-dev - - librsvg2-dev - - libmagickcore-6.q16-dev - - libexif-dev - - libgsf-1-dev - - liblcms2-dev - - libpng12-dev - - libfontconfig1-dev - - libgdk-pixbuf2.0-dev - - libfreetype6-dev - - libxft-dev - - libcairo2-dev - - libpango1.0-dev - - liborc-0.4-dev - - libwebp-dev - - nip2-part: - source: https://github.com/jcupitt/nip2 - source-branch: master - source-type: git - plugin: autotools - build-packages: - - gobject-introspection - - intltool - - libgirepository1.0-dev - - libglib2.0-dev - - libgtk2.0-dev - - libx11-dev - - libxml2-dev - - flex - - bison - - libfftw3-dev - - libgoffice-0.8-dev - - libgsl-dev - - libgvc6 - after: [desktop-gtk2, vips-part] - From bf751e8317c3015356adf0d28d4abcb34aa53f5e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 28 Mar 2024 11:57:13 +0000 Subject: [PATCH 32/37] stray memory alloc --- src/imageinfo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/imageinfo.c b/src/imageinfo.c index 2abf10f8..14f70f92 100644 --- a/src/imageinfo.c +++ b/src/imageinfo.c @@ -720,7 +720,6 @@ imageinfo_proxy_add( Imageinfo *imageinfo ) */ g_assert( !imageinfo->proxy ); if( !(imageinfo->proxy = IM_NEW( imageinfo->im, Imageinfoproxy )) ) - if( !(imageinfo->proxy = IM_NEW( NULL, Imageinfoproxy )) ) return; imageinfo->proxy->im = imageinfo->im; imageinfo->proxy->imageinfo = imageinfo; From dbbfc9e3866ea9b63734dd2ae78d8c9d7d109697 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 29 Jul 2024 14:19:05 +0100 Subject: [PATCH 33/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7eef5c12..fdc3dd52 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ analyzing images of many tens of gigabytes. It has a batch mode, so you can run any image processing system you develop from the command-line and without a GUI. -[![Screenshot](screenshot.png)](screenshot.png) +![image](https://github.com/user-attachments/assets/aa2c3e0f-9f96-4594-9f1d-62ef770d0775) ## Installing From 6149a927f9fdc631c11b5e23cdd1f8da8bb86de8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 29 Jul 2024 14:19:43 +0100 Subject: [PATCH 34/37] Delete screenshot.png --- screenshot.png | Bin 233031 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screenshot.png diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 224dfebef7f68160767c11ac124abec6c6329660..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233031 zcmd3Oby!qu*Ec1Il!SCh2qN7LN+TuG-Q6HPG}4W9sUV0TDGV_*Lk-MGcelXMGc+H1 z&huR7yzlq__wDOq@40U7eebo_UUmQ0nu&R%rhxZ|@(~IO3Z9ap>{}ERtPm6wjOvG& z_al_tx`_J=wwt29CkhG<(eFRh(S@Mv`#}bOExmV|t~T#{%ss48P#$J0!yqu#2@W;R z*Q(0(pd6hRZru!RSRFZZ)Kf6tcjur_~Z@9OeMHfXsa@la8$y)Y|NYS9W&P@bYF$-dI^&D&e>0T^v$Bky3@pBG!Klp3s_AQzoG z9#|T2n?3!&{_Lf+;==Ieg>_wS!_0@R%-R-u`uEAn5kz5lCFGV=@$TsXuBpy5I++tK z{5jbt*YpD!*6AtFnxYS`&W>?xHb3mnZ^c8spaRIi_~Y0Y#DxDkns#P#a;T@oF#k15 zxKmV8!puG;_V9lUw_IL|J_(VN{pW!hzG+kXsDt5OM|h}x&I^>d=zku#r2lJ#s9~2T zn9ChkRKcd}V ziIj5Czm2$1N!(iko2n0&3LdSp^||*AGg=?lbD;=rxvXsHHoPIv>La`J!Y(rj(H};T z1%bbLV$UPL_hoz5k+KgPAleWLCSo?Ji>J7Sg@m)EpE`#&MF(Fy)X2*dI~;)H?`hqR zEAgxdG=FATZmEAf-aSv`i4JD|Ls)p?e!m7l_j_bX31kttNoFz3)e{haiAPY;A!74m z3$l0c^zYZ=VqhaeabWvq`3IqtGw*;23g@p_kNzrw&I;zMpF9kjL`tzZT@ZGgn<^7C zV^k(izlypcn9@T#JaLp#3;q+XRR|qgP;?73C<>Li*s#|#+d^_Th*c@5CN2P8|YuN6gk$#39 z9NuxjjxuSrpxu-=R8=$*SdiwYIuvtMv_9Z5f|#XLxsrfyM7X3k_)RnOZlMezy)`_~u_mnj%k z{!-`SBqNc48Se%Pz*v?T<0CfJyoEy|sf$8wE0U&8ZD^=K`T0$isikI0%M?V&kyrmL zm(1^?s5_6f@>9)>oTrR{Tq!{fJYDK|SHSjbi|@ig$+ypxKx`XNNkd6Vxo$8_9OrW|wb+I@M za$c(+4M>@+Zw1f3_Fm9dm}Sf8AE;=w#RqbLa$9=+-d;cG+ez|Wam}3$XvmX~qbaXT z#IZ%3Oe#By7zI18lK!@Ulw0#0<~csZvWM4k_iT{W_+!6qndYfw&B!<{@Hh(p_yyei{3kRA;ryyB; z8t}C>P8QMHglI6jNQ=->X9IB_$M?QdZ1e5CmW%6o{9X6->U|j3k&X`fV)y3J-WV{| zuAIRW!Yn!O89N^oIclLf|KOawpevCe zU@K+&PT>X?vee9rtZpsgY;TCQ=yO9sugxKLobBFspK5>syQKUa^JbmpFs7tPj{29e z%;?9&WJdbFe7nk9=}_a2H~ z5LaOKh78^dH9*&|Gb|BpqbLX@Df#Qj{7nyo{FRSUcpQk4h1HYlyd=r`Lwl%x;0L_i zldpYA>mr`ngsxvceGBWdFqJ=T=quNUtsoZ}iyA2aQQTW_@DSaT7~Md^D8V+ESO8aY z`@Y0vi4+9gelbMJOPhm3KqoDyTWq?OJ7KO1&+SQ_CB&Z6@PN4STX~rP{od+eMGx1t zbPMgkz8tro2iool)csbGd85wu@nRtJQs?$K+1(HjRW&Ch?IuhPa2lr@b zzZG?awp7nL>kTD7gO@K4@azm)U(Gu2IX~)Fq#0O<`SShb!&@C~g*1DX=lgq44&xZ4m19xh;n);3{{TXw^OF$dRLpD!2MzI>vG(j5hN-?Ozei zW4}>Zq~VIZH136c?K#o1<*3;a{ZG5qq1nufHLNHRT}%4s)43(7d`^LDPfVn==0;^k zjFk{IeCrvLqfRGa*rduuQ#hd7hAIk&vf7_Y_}e3qA3;yblLUVZSqi#e-n1V@AqhN6 zhC;zEKi{MdCr$*fy-&h%rwl4idfnraHEw0Axr{kvpjOxxLd7N{U-EcW>0bMD%hNqtSnwZlNK!@2VxWuCH!@<9i9V>ow?8Ua}=@Nuj7!Bg9E?Z{B63fV!i!nR+{oBs+^OPexJ~C?m}@E^3Z`|f*K1QI*W;N zL*ch?q6C;yv&^W@wAJ<`^R>i>9iSUpD}ohFxau8L+>YgC9;g{+Z%fszeytD8i{5$D z0(=E!0{jR%DnwrOju@zPLl5PUbrv4tpV}7BqX?`g1CcyZ(AbsA^$z$LKiYC}LkS`G zLtOk0x6SH61x~e(JdR`(axQ={^drFzW8J7N8Y{f8v?xMrRNWwak;FykzVx^{;>V<~ zw6ri%=q0}kQTmHE%u#;HJ&1Z1KXBq=ecPIJJPb=@`IuymRzQ~91sHwru>=R&WT%+TSk2#qk>jney*A1Sumv7r%YsheHJ3_(nU z=wLm9O!#d`*Q})*(uN3MYJFSI++OeFVg-l}f!SZV3?iO;Q~b;tPHWS-;bB=O((x@G zr_l+U{pNCwJ-Xpd)9%6boom7BcIBE@LSZjBG&%l_O)wn7o;0{RVG8POGho|cYlD)N z2oIc(x$32B!^(!x<}ITv%S4BwD4q!?m@!LUGZp49t6wBeT`U3B)9S3n`fQe9jf9Nk zHe#weGxlmEFc$jfxx^xBDvs2a^6xFQ_HAX9~9}ObyjFhuv?F=FY6r3s_oanHK#cybN*NmSNig1t;SgN=)sr#5fu=N%0&I;&;Q(4@{JixaF%%E;favSuz<~&{& zas*R`_{qaMY4$8mG8VJ_(6lRWPp2Smuen4GOYF4_!$?oSzRTe3qYg5%Ssq#(4QCiOCcF8Htlq~C zeI@;Ls>|bR6J-+5!e8gO%kZTAh>ixoma8CgjHI>%>kzix^NGHxY?s6V&fUxJ!2xjE zU(0*VnHLKsXthH7PaS&~;+=fBv+zVJf}M$H)Nnp&x>)-c2L*8b&O=vYf-C&1#*lQN zyGlpOD0A|NNDK`PebwK77B1N_w3BvuliiVV91Ag_f=Q^K2(?8@a6Aav9J8&tMpoS` zvVj47q*fc{uasoBzr&$^mA#*_ib91iQ=t8v&~aAPyzJ}8EiHRA7O(kb zd}@3Jjr|#zq^0BGYm2?ZjL+p0lU`E>%CawtJ`1K8=E0h-r7O06@c;r39oScfnmoc_ zPu2uMiZlYat|8kb@^V&gpU(NqgKpUniTOR>Jxlo{OVYfu@N=k{$xVC%>UG}k%jhHC zV;uz^Fg8%Pcg&z0iUhc^&?T7;r4gtxlq{UJo0F68mp7Nhw8&J=XJkeyz|z4^A1gaC zx+6_beVLOW%j*Ry4HfmHZoQ!#?vLumat3Zu>>NIJf~|Eg1eZ{*TW8VuAD+4Zhjm;* z`!6;5RCV70ygW`1@~OuPl}@gi!&;i>EU!{s+UVR{o0>e*d3-!n5Yshtht|_W*tiRC zJ9aLmJs|V(P7;sjCrbSexY%a$ww^vc!_=eF=409`Tl@KoijcjrB_8|GrvORQ6jB{!cTq5niO$!5<*fpzsH98 zrE@zT^)8X2V6=ojsnvS*+A^f786vfe$&ak&W}n7N%)X4Ip)xn|Js7fzWM=A-)|Nw$ znyOq|t`5j1w`dy2)a?%UZqeK0&(AcDL-aT}w&J#un>zB0Z**-ab!0r3PrzO4?s}wl zAQ7Tr!~O1-I;_EYeS82H1QY9I%O7D~^AeFt&B3!< z@DX=swjjXiXs9E0($mpLPoOwf1(h~IT8b_BEnlqi%epW?3|)pv7tEBDs@#CXlOuZP zP1(;#XZipnJvV?t%2AxKP2q^AgWs?euaQK39>mh@i^**!2!sn!nINFdhaKSMD)C8w z@dmN6l`Hq}Mt&;(ylM6IZ=6Oix3KC&)3k&22B1V~KWluBE~1zTJ?ov>emj+8)_30n zE*om}*oSg>-siU<($X^3t>rcZ^sCs!aUEZ+d=YB939bhBEDKNa4%JO<6Y>XN<&sNX zw`UHoVgHRstT)%mgC9Fe4C(9A4AU27OEFT?q33y}kI#f*w++(1o1FF~a*FEzCN^Bj zs9mgbSPHl!tj#w&?@j(G%J0~*9TR^9voLkRq%+80&WD~Jae$i&EcqV#ozGn(yJ>Xb zplf}}tc1KJzvL56ZZr#}hfz6c8~izDG`t1x>v{>-5nHFT@fi#hW-+n!z5ZFAUZvB1 z)nV%cpQhbMrnh@r_8p*yw%37>nyP^G1V07+3xDdG!a^l2E&dT5VN;(Ri6{t#U~k+Y zC5#sFOQXf_f_U5crSXF;PY1U?;{{@a?;|`t1_UX_KC$uW8^vm^#}<>uDFcybQDyNt zL#ciPxD;t>J)8UM2T~ZaHFiV3(kFtR=RL0y8&yH_30B3XyRX(9K0>3dXH&7Vy#sit zZV%Du)=511>+;?oX=IWqBs`T4=XhhRp#L77*c3NUn3@y>VxJBt&Z^xG0_JRp@=mMcDgJ6zreLukZ(f%d}lym!Ud;_^5eDA;vc6bf>dR$ z{GHWn8`k$?ikVTO0C_c2FutSql)p0PpF2Wp{ERX$;Uo88gOj`BRRpG2MZ+xl*bL8t zd%&LL`D{qZi$`&}+(m%S4YcxCs`kh}#FNVNpwkAlJ^i7;wcZUuRI(?1d;?ygE5xr< zvc7ESPr9}aLy5><6=j})6_|-;Ahxo9Qmpv>ob?_7&R~@0kC~w={*8}hl?g0iIU`IRCzWCA`!Mr%lo5uyC>2ogf zIUZbjfoMfgU(LQB*EAli^S<9>Tbi=h^ae;^~zBPbr8KsK-U`7JpG@yq`0Pt)oJ zy`e%oYnn}Zcf(7Ap6zoonr`IuZEAyYMrP-aNlD$T6>OCs#vI*}iwLY2aCC_H2{+VA zT(9VgxiK+4CL@e5Y>6jUpyI(}8{r|;6Q?w2akfOyY6P&d;ONvd z&J@ZD=VmB2p2Vu0C{*55W*B^aiz;fA7Z@s8uWM;=_ond-9(AbhlWj5_bd#M!OAv0CoieV@1}c-LKOB z0-c)8&iVYo#DJh9-$;c^bN6x0B_~EE!NkY8bp$dLSIOQ}mg0e1dXevE-379J@3%CU z#!1IRUGzrZcxDuH1QN`>xhqD7veLrTO)cv%_?&vUSG%sO`6u2kSkXThOfaPL4J&m` z=z26A1>1_*AXa;l)3P79Fi_p=FU=n)D2uS310>I!IV@%UFb?F8)oE;ziMWz8+299J`TaF< zzGmK5Rb1aV=!^W;9o3HyK_g5G50;(lEKabQ{Ll4BC9g?LO;hB}49!$k`O(a}U9h+h zRdZouroJDdO6u|Vou+FHQGwwl_*m~-W?sq2j9K61=}Q8JQWdfo4ICBWN zbi5mTqpfB?^Cfu0tCgJ5_>XE-vUP{-j4>Ofd1a&C@{3o8RTf^1Cz}6syKBt~3Zmth zd(Qr%=~n>j?+D7k;8DbD|FxorhCH_YiCad^28`7QD6H205FTG1?FG9@ z!F#Xs_TYxdG>;<8T(pc0Vm{_rRa^5kQqD9mUQPxi!R4`Jmg6aOyaaRoGR>9_1FR9+ zM9fzhdL#(*`JF`t+q!C!dlugC`Vh3xL&pAkbXqNqMC8FY;K!P>6YP5xZ@e?fvy>0` z0a{(Pz(eKuGl}DA5f5vgN1c{Pa7@ z9;Z2k?QR*&At#JFB>$++bD-Q~(f0voT?XG(Qkwqk{Bg789>1n`R$o)?rX$tW6L-LC zBlik^jZ{NL4QH}GXhLoK<*NK`(4RN{_o@G*+3xKcCVpj5+}V~f%_1-rbSMd@vu1h9 zlvRzw6_1KjBjm0honM!yP>OLzoisx4txM4|>9pBZyn%}cM2d8+Iht(Uq>|7EIXya> zgh+Wl+1mhX&-8G$WPVYaD#vRvo2+r2aF87I@wsghNE9n<&OZv8608VaG+l5MSrnYL zIhtrmJ#;`Sc~06C7#^72rr~67OLdt4lgjARk5OoDIJ?P5^HO3B3q6>U^o!(?1Zf!y z1I4H~RY-Tg2NwZ4iUQJ@{D>9C$mRUN0^$e6G3s`vsNNdM=`!`E3`Ioy=(Gw|6q3cm z#)?*H*jXQ_XM6j=HS9)N(pjkFixC$_NsTVm-b8{VUE#U_E-^Jyjkk`gWa<50nL0i)s$pV@o~R2jfq)TOnt*_n@h%0A^k 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