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