1/* A list of things. Do automatic iteration of unary and binary operators on 2 * us. 3 * List [1, 2] + [2, 3] -> List [3, 5] 4 * hd (List [2, 3]) -> 2 5 * List [] == [] -> true 6 */ 7List value = class 8 _Object { 9 _check_args = [ 10 [value, "value", check_list] 11 ] ++ super._check_args; 12 13 // methods 14 oo_binary_table op x = [ 15 [apply2 op value x', 16 op.op_name == "subscript" || op.op_name == "subscript'" || 17 op.op_name == "equal" || op.op_name == "equal'"], 18 [this.List (apply2 op value x'), 19 op.op_name == "join" || op.op_name == "join'"], 20 [this.List (map2 (apply2 op) value x'), 21 is_list x'], 22 [this.List (map (apply2 op' x) value), 23 true] 24 ] ++ super.oo_binary_table op x 25 { 26 op' = oo_converse op; 27 28 // strip the List wrapper, if any 29 x' 30 = x.value, is_List x 31 = x; 32 33 apply2 op x1 x2 34 = oo_binary_function op x1 x2, is_class x1 35 = oo_binary'_function op x1 x2, is_class x2 36 = op.fn x1 x2; 37 }; 38 39 oo_unary_table op = [ 40 [apply value, 41 op.op_name == "hd" || op.op_name == "tl"], 42 [this.List (map apply value), 43 true] 44 ] ++ super.oo_unary_table op 45 { 46 apply x 47 = oo_unary_function op x, is_class x 48 = op.fn x; 49 } 50} 51 52/* A group of things. Loop the operation over the group. 53 */ 54Group value = class 55 _Object { 56 _check_args = [ 57 [value, "value", check_list] 58 ] ++ super._check_args; 59 60 // methods 61 oo_binary_table op x = [ 62 // if_then_else is really a trinary operator 63 [map_trinary ite this x?0 x?1, 64 op.op_name == "if_then_else"], 65 [map_binary op.fn this x, 66 is_Group x], 67 [map_unary (\a op.fn a x) this, 68 true] 69 ] ++ super.oo_binary_table op x; 70 71 oo_unary_table op = [ 72 [map_unary op.fn this, 73 true] 74 ] ++ super.oo_unary_table op; 75 76 // we can't call map_trinary directly, since it uses Group and we 77 // don't support mutually recursive top-level functions :-( 78 // copy-paste it here, keep in sync with the version in _stdenv 79 map_nary fn args 80 = fn args, groups == [] 81 = Group (map process [0, 1 .. shortest - 1]) 82 { 83 groups = filter is_Group args; 84 85 shortest = foldr1 min_pair (map (len @ get_value) groups); 86 87 process n 88 = NULL, any (map (is_noval n) args) 89 = map_nary fn (map (extract n) args) 90 { 91 extract n arg 92 = arg.value?n, is_Group arg 93 = arg; 94 95 is_noval n arg = is_Group arg && arg.value?n == NULL; 96 } 97 } 98 99 // need ite as a true trinary 100 ite a b c = if a then b else c; 101 102 map_unary fn a = map_nary (list_1ary fn) [a]; 103 map_binary fn a b = map_nary (list_2ary fn) [a, b]; 104 map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; 105} 106 107/* Single real number ... eg slider. 108 */ 109Real value = class 110 _Object { 111 _check_args = [ 112 [value, "value", check_real] 113 ] ++ super._check_args; 114 115 // methods 116 oo_binary_table op x = [ 117 [this.Real (op.fn this.value x.value), 118 is_Real x && 119 op.type == Operator_type.ARITHMETIC], 120 [this.Real (op.fn this.value x), 121 is_real x && 122 op.type == Operator_type.ARITHMETIC], 123 [op.fn this.value x.value, 124 is_Real x && 125 op.type == Operator_type.RELATIONAL], 126 [op.fn this.value x, 127 !is_class x] 128 ] ++ super.oo_binary_table op x; 129 130 oo_unary_table op = [ 131 [this.Real (op.fn this.value), 132 op.type == Operator_type.ARITHMETIC], 133 [op.fn this.value, 134 true] 135 ] ++ super.oo_unary_table op; 136} 137 138/* Single bool ... eg Toggle. 139 */ 140Bool value = class 141 _Object { 142 _check_args = [ 143 [value, "value", check_bool] 144 ] ++ super._check_args; 145 146 // methods 147 oo_binary_table op x = [ 148 [op.fn this.value x, 149 op.op_name == "if_then_else"], 150 [this.Bool (op.fn this.value x.value), 151 is_Bool x], 152 [this.Bool (op.fn this.value x), 153 is_bool x] 154 ] ++ super.oo_binary_table op x; 155 156 oo_unary_table op = [ 157 [this.Bool (op.fn this.value), 158 op.type == Operator_type.ARITHMETIC || 159 op.type == Operator_type.RELATIONAL], 160 [op.fn this.value, 161 true] 162 ] ++ super.oo_unary_table op; 163} 164 165/* An editable string. 166 */ 167String caption value = class 168 _Object { 169 _check_args = [ 170 [caption, "caption", check_string], 171 [value, "value", check_string] 172 ] ++ super._check_args; 173} 174 175/* An editable real number. 176 */ 177Number caption value = class 178 scope.Real value { 179 _check_args = [ 180 [caption, "caption", check_string] 181 ] ++ super._check_args; 182 183 Real x = this.Number caption x; 184} 185 186/* An editable expression. 187 */ 188Expression caption expr = class 189 (if is_class expr then expr else _Object) { 190 _check_args = [ 191 [caption, "caption", check_string], 192 [expr, "expr", check_any] 193 ] ++ super._check_args; 194} 195 196/* A ticking clock. 197 */ 198Clock interval value = class 199 scope.Real value { 200 _check_args = [ 201 [interval, "interval", check_real] 202 ] ++ super._check_args; 203 204 Real x = this.Clock interval x; 205} 206 207/* An editable filename. 208 */ 209Pathname caption value = class 210 _Object { 211 _check_args = [ 212 [caption, "caption", check_string], 213 [value, "value", check_string] 214 ] ++ super._check_args; 215} 216 217/* An editable fontname. 218 */ 219Fontname caption value = class 220 _Object { 221 _check_args = [ 222 [caption, "caption", check_string], 223 [value, "value", check_string] 224 ] ++ super._check_args; 225} 226 227/* Vector type ... just a finite list of real. Handy for wrapping an 228 * argument to eg. im_lintra_vec. Make it behave like a single pixel image. 229 */ 230Vector value = class 231 _Object { 232 _check_args = [ 233 [value, "value", check_real_list] 234 ] ++ super._check_args; 235 236 bands = len value; 237 238 // methods 239 oo_binary_table op x = [ 240 // Vector ++ Vector means bandwise join 241 [this.Vector (op.fn this.value x.value), 242 is_Vector x && 243 (op.op_name == "join" || op.op_name == "join'")], 244 [this.Vector (op.fn this.value [get_number x]), 245 has_number x && 246 (op.op_name == "join" || op.op_name == "join'")], 247 // Vector ? number means extract element 248 [op.fn this.value (get_real x), 249 has_real x && 250 (op.op_name == "subscript" || 251 op.op_name == "subscript'")], 252 // extra check for lengths equal 253 [this.Vector (map_binaryl op.fn this.value x.value), 254 is_Vector x && 255 len value == len x.value && 256 op.type == Operator_type.ARITHMETIC], 257 [this.Vector (map_binaryl op.fn this.value (get_real x)), 258 has_real x && 259 op.type == Operator_type.ARITHMETIC], 260 261 // need extra length check 262 [this.Vector (map bool_to_real 263 (map_binaryl op.fn this.value x.value)), 264 is_Vector x && 265 len value == len x.value && 266 op.type == Operator_type.RELATIONAL], 267 [this.Vector (map bool_to_real 268 (map_binaryl op.fn this.value (get_real x))), 269 has_real x && 270 op.type == Operator_type.RELATIONAL], 271 [this.Vector (op.fn this.value x.value), 272 is_Vector x && 273 len value == len x.value && 274 op.type == Operator_type.COMPOUND_REWRAP], 275 [x.Image (vec op'.op_name x.value value), 276 is_Image x], 277 [vec op'.op_name x value, 278 is_image x], 279 [op.fn this.value x, 280 is_real x] 281 ] ++ super.oo_binary_table op x 282 { 283 op' = oo_converse op; 284 }; 285 286 oo_unary_table op = [ 287 [this.Vector (map_unaryl op.fn this.value), 288 op.type == Operator_type.ARITHMETIC], 289 [this.Vector (map bool_to_real 290 (map_unaryl op.fn this.value)), 291 op.type == Operator_type.RELATIONAL], 292 [this.Vector (op.fn this.value), 293 op.type == Operator_type.COMPOUND_REWRAP], 294 [op.fn this.value, 295 true] 296 ] ++ super.oo_unary_table op; 297 298 // turn an ip bool (or a number, for Vector) into VIPSs 255/0 299 bool_to_real x 300 = 255, is_bool x && x 301 = 255, is_number x && x != 0 302 = 0; 303} 304 305/* A rectangular array of real. 306 */ 307Matrix_base value = class 308 _Object { 309 _check_args = [ 310 [value, "value", check_matrix] 311 ] ++ super._check_args; 312 313 // calculate these from value 314 width = len value?0; 315 height = len value; 316 317 // extract a rectanguar area 318 extract left top width height 319 = this.Matrix_base 320 ((map (take width) @ map (drop left) @ 321 take height @ drop top) value); 322 323 // methods 324 oo_binary_table op x = [ 325 // mat multiply is special 326 [this.Matrix_base mul.value, 327 is_Matrix x && 328 op.op_name == "multiply"], 329 [this.Matrix_base mul'.value, 330 is_Matrix x && 331 op.op_name == "multiply'"], 332 333 // mat divide is also special 334 [this.Matrix_base div.value, 335 is_Matrix x && 336 op.op_name == "divide"], 337 [this.Matrix_base div'.value, 338 is_Matrix x && 339 op.op_name == "divide'"], 340 341 // power -1 means invert 342 [this.Matrix_base inv.value, 343 is_real x && x == -1 && 344 op.op_name == "power"], 345 [this.Matrix_base sq.value, 346 is_real x && x == 2 && 347 op.op_name == "power"], 348 [error "matrix **-1 and **2 only", 349 op.op_name == "power" || 350 op.op_name == "power'"], 351 352 // matrix op vector ... treat a vector as a 1 row matrix 353 [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), 354 is_Vector x && 355 op.type == Operator_type.ARITHMETIC], 356 [this.Matrix_base (map_binaryl op.fn this.value x.value), 357 (is_Matrix x || is_Real x) && 358 op.type == Operator_type.ARITHMETIC], 359 360 [this.Matrix_base (map_binaryl op.fn this.value x), 361 is_real x && 362 op.type == Operator_type.ARITHMETIC], 363 364 // compound ... don't do iteration 365 [this.Matrix_base (op.fn this.value x.value), 366 (is_Matrix x || is_Real x || is_Vector x) && 367 op.type == Operator_type.COMPOUND_REWRAP] 368 ] ++ super.oo_binary_table op x 369 { 370 mul = im_matmul this x; 371 mul' = im_matmul x this; 372 div = im_matmul this (im_matinv x); 373 div' = im_matmul x (im_matinv this); 374 inv = im_matinv this; 375 sq = im_matmul this this; 376 op' = oo_converse op; 377 } 378 379 oo_unary_table op = [ 380 [this.Matrix_base (map_unaryl op.fn this.value), 381 op.type == Operator_type.ARITHMETIC], 382 [this.Matrix_base (op.fn this.value), 383 op.type == Operator_type.COMPOUND_REWRAP], 384 [op.fn this.value, 385 true] 386 ] ++ super.oo_unary_table op; 387} 388 389/* How to display a matrix: text, sliders, toggles, or text plus scale/offset. 390 */ 391Matrix_display = class { 392 text = 0; 393 slider = 1; 394 toggle = 2; 395 text_scale_offset = 3; 396 397 is_display = member [text, slider, toggle, text_scale_offset]; 398} 399 400/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add 401 * a display type as well to control how the widget renders. 402 */ 403Matrix_vips value scale offset filename display = class 404 scope.Matrix_base value { 405 _check_args = [ 406 [scale, "scale", check_real], 407 [offset, "offset", check_real], 408 [filename, "filename", check_string], 409 [display, "display", check_matrix_display] 410 ] ++ super._check_args; 411 412 Matrix_base x = this.Matrix_vips x scale offset filename display; 413} 414 415/* A plain 'ol matrix which can be passed to VIPS. 416 */ 417Matrix value = class 418 Matrix_vips value 1 0 "" Matrix_display.text {} 419 420/* Specialised constructors ... for convolutions, recombinations and 421 * morphologies. 422 */ 423Matrix_con scale offset value = class 424 Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; 425 426Matrix_rec value = class 427 Matrix_vips value 1 0 "" Matrix_display.slider {}; 428 429Matrix_mor value = class 430 Matrix_vips value 1 0 "" Matrix_display.toggle {}; 431 432Matrix_file filename = (im_read_dmask @ expand @ search) filename; 433 434/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) 435 */ 436Colour colour_space value = class 437 scope.Vector value { 438 _check_args = [ 439 [colour_space, "colour_space", check_colour_space] 440 ] ++ super._check_args; 441 _check_all = [ 442 [is_list_len 3 value, "len value == 3"] 443 ] ++ super._check_all; 444 445 Vector x = this.Colour colour_space x; 446 447 // make a colour-ish thing from an image 448 // back to Colour if we have another 3 band image 449 // to a vector if bands > 1 450 // to a number otherwise 451 itoc im 452 = this.Colour nip_type (to_matrix im).value?0, 453 bands == 3 454 = scope.Vector (map mean (bandsplit im)), 455 bands > 1 456 = mean im 457 { 458 type = get_header "Type" im; 459 bands = get_header "Bands" im; 460 nip_type = Image_type.colour_spaces.lookup 1 0 type; 461 } 462 463 // methods 464 oo_binary_table op x = [ 465 [itoc (op.fn 466 ((float) (to_image this).value) 467 ((float) (to_image x).value)), 468 // here REWRAP means go via image 469 op.type == Operator_type.COMPOUND_REWRAP] 470 ] ++ super.oo_binary_table op x; 471 472 oo_unary_table op = [ 473 [itoc (op.fn ((float) (to_image this).value)), 474 op.type == Operator_type.COMPOUND_REWRAP] 475 ] ++ super.oo_unary_table op; 476} 477 478// a subclass with widgets for picking a space and value 479Colour_picker default_colour default_value = class 480 Colour space.value_name colour.expr { 481 _vislevel = 3; 482 483 space = Option_enum Image_type.colour_spaces "Colour space" default_colour; 484 colour = Expression "Colour value" default_value; 485 486 Colour_edit colour_space value = 487 Colour_picker colour_space value; 488} 489 490/* Base scale type. 491 */ 492Scale caption from to value = class 493 scope.Real value { 494 _check_args = [ 495 [caption, "caption", check_string], 496 [from, "from", check_real], 497 [to, "to", check_real] 498 ] ++ super._check_args; 499 _check_all = [ 500 [from < to, "from < to"] 501 ] ++ super._check_all; 502 503 Real x = this.Scale caption from to x; 504 505 // methods 506 oo_binary_table op x = [ 507 [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) 508 (op.fn this.value x.value), 509 is_Scale x && 510 op.type == Operator_type.ARITHMETIC], 511 [this.Scale caption (op.fn this.from x) (op.fn this.to x) 512 (op.fn this.value x), 513 is_real x && 514 op.type == Operator_type.ARITHMETIC] 515 ] ++ super.oo_binary_table op x; 516} 517 518/* Base toggle type. 519 */ 520Toggle caption value = class 521 scope.Bool value { 522 _check_args = [ 523 [caption, "caption", check_string], 524 [value, "value", check_bool] 525 ] ++ super._check_args; 526 527 Bool x = this.Toggle caption x; 528} 529 530/* Base option type. 531 */ 532Option caption labels value = class 533 scope.Real value { 534 _check_args = [ 535 [caption, "caption", check_string], 536 [labels, "labels", check_string_list], 537 [value, "value", check_uint] 538 ] ++ super._check_args; 539} 540 541Option_enum enum caption value_name = class 542 Option caption enum.names (index (equal value_name) enum.names) { 543 // corresponding thing 544 value_thing = enum.get_thing value_name; 545 546 Option_edit caption labels value 547 = this.Option_enum enum caption (enum.names ? value); 548} 549 550/* A rectangle. width and height can be -ve. 551 */ 552Rect left top width height = class 553 _Object { 554 _check_args = [ 555 [left, "left", check_real], 556 [top, "top", check_real], 557 [width, "width", check_real], 558 [height, "height", check_real] 559 ] ++ super._check_args; 560 561 // derived 562 right = left + width; 563 bottom = top + height; 564 565 oo_binary_table op x = [ 566 [equal x, 567 is_Rect x && 568 (op.op_name == "equal" || op.op_name == "equal'")], 569 [!equal x, 570 is_Rect x && 571 (op.op_name == "not_equal" || 572 op.op_name == "not_equal'")], 573 574 // binops with a complex are the same as (comp op comp) 575 [oo_binary_function op this (Rect (re x) (im x) 0 0), 576 is_complex x], 577 578 // all others are just pairwise 579 [this.Rect left' top' width' height', 580 is_Rect x && 581 op.type == Operator_type.ARITHMETIC], 582 [this.Rect left'' top'' width'' height'', 583 has_number x && 584 op.type == Operator_type.ARITHMETIC] 585 ] ++ super.oo_binary_table op x 586 { 587 left' = op.fn left x.left; 588 top' = op.fn top x.top; 589 width' = op.fn width x.width; 590 height' = op.fn height x.height; 591 592 left'' = op.fn left x'; 593 top'' = op.fn top x'; 594 width'' = op.fn width x'; 595 height'' = op.fn height x'; 596 x' = get_number x; 597 } 598 599 oo_unary_table op = [ 600 // arithmetic uops just map 601 [this.Rect left' top' width' height', 602 op.type == Operator_type.ARITHMETIC], 603 604 // compound uops are just like ops on complex 605 // do (width, height) so thing like abs(Arrow) work as you'd expect 606 [op.fn (width, height), 607 op.type == Operator_type.COMPOUND] 608 ] ++ super.oo_unary_table op 609 { 610 left' = op.fn left; 611 top' = op.fn top; 612 width' = op.fn width; 613 height' = op.fn height; 614 } 615 616 // empty? ie. contains no pixels 617 is_empty = width == 0 || height == 0; 618 619 // normalised version, ie. make width/height +ve and flip the origin 620 nleft 621 = left + width, width < 0 622 = left; 623 ntop 624 = top + height, height < 0 625 = top; 626 nwidth = abs width; 627 nheight = abs height; 628 nright = nleft + nwidth; 629 nbottom = ntop + nheight; 630 631 equal x = left == x.left && top == x.top && 632 width == x.width && height == x.height; 633 634 // contains a point? 635 includes_point x y 636 = nleft <= x && x <= nright && ntop <= y && y <= nbottom; 637 638 // contains a rect? just test top left and bottom right points 639 includes_rect r 640 = includes_point r.nleft r.ntop && 641 includes_point r.nright r.nbottom; 642 643 // bounding box of two rects 644 // if either is empty, can just return the other 645 union r 646 = r, is_empty 647 = this, r.is_empty 648 = Rect left' top' width' height' 649 { 650 left' = min_pair nleft r.nleft; 651 top' = min_pair ntop r.ntop; 652 width' = max_pair nright r.nright - left'; 653 height' = max_pair nbottom r.nbottom - top'; 654 } 655 656 // intersection of two rects ... empty rect if no intersection 657 intersect r 658 = Rect left' top' width'' height'' 659 { 660 left' = max_pair nleft r.nleft; 661 top' = max_pair ntop r.ntop; 662 width' = min_pair nright r.nright - left'; 663 height' = min_pair nbottom r.nbottom - top'; 664 width'' 665 = width', width > 0 666 = 0; 667 height'' 668 = height', height > 0 669 = 0; 670 } 671 672 // expand/collapse by n pixels 673 margin_adjust n 674 = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); 675} 676 677/* Values for Compression field in image. 678 */ 679Image_compression = class { 680 NONE = 0; 681 NO_COMPRESSION = 0; 682 TCSF_COMPRESSION = 1; 683 JPEG_COMPRESSION = 2; 684 LABPACK_COMPRESSED = 3; 685 RGB_COMPRESSED = 4; 686 LUM_COMPRESSED = 5; 687} 688 689/* Values for Coding field in image. 690 */ 691Image_coding = class { 692 NONE = 0; 693 NOCODING = 0; 694 COLQUANT = 1; 695 LABPACK = 2; 696} 697 698/* Values for BandFmt field in image. 699 */ 700Image_format = class { 701 DPCOMPLEX = 9; 702 DOUBLE = 8; 703 COMPLEX = 7; 704 FLOAT = 6; 705 INT = 5; 706 UINT = 4; 707 SHORT = 3; 708 USHORT = 2; 709 CHAR = 1; 710 UCHAR = 0; 711 NOTSET = -1; 712 713 maxval fmt 714 = [ 715 255, // UCHAR 716 127, // CHAR 717 65535, // USHORT 718 32767, // SHORT 719 4294967295, // UINT 720 2147483647, // INT 721 255, // FLOAT 722 255, // COMPLEX 723 255, // DOUBLE 724 255 // DPCOMPLEX 725 ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX 726 = error (_ "bad value for BandFmt"); 727} 728 729/* A lookup table. 730 */ 731Table value = class 732 _Object { 733 _check_args = [ 734 [value, "value", check_rectangular] 735 ] ++ super._check_args; 736 737 /* present col x: is there an x in column col 738 */ 739 present col x = member (map (extract col) value) x; 740 741 /* Look on column from, return matching item in column to. 742 */ 743 lookup from to x 744 = value?n?to, n >= 0 745 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") 746 { 747 n = index (equal x) (map (extract from) value); 748 } 749} 750 751/* A two column lookup table with the first column a string and the second a 752 * thing. Used for representing various enums. Option_enum makes a selector 753 * from one of these. 754 */ 755Enum value = class 756 Table value { 757 _check_args = [ 758 [value, "value", check_enum] 759 ] ++ super._check_args 760 { 761 check_enum = [is_enum, _ "is [[char, *]]"]; 762 is_enum x = 763 is_rectangular x && 764 is_listof is_string (map (extract 0) x); 765 } 766 767 // handy ... all the names and things as lists 768 names = map (extract 0) value; 769 things = map (extract 1) value; 770 771 // is a legal name or thing 772 has_name x = this.present 1 x; 773 has_thing x = this.present 0 x; 774 775 // map things to strings and back 776 get_name x = this.lookup 1 0 x; 777 get_thing x = this.lookup 0 1 x; 778} 779 780/* Type field. 781 */ 782Image_type = class { 783 MULTIBAND = 0; 784 B_W = 1; 785 LUMINANCE = 2; 786 XRAY = 3; 787 IR = 4; 788 YUV = 5; 789 RED_ONLY = 6; 790 GREEN_ONLY = 7; 791 BLUE_ONLY = 8; 792 POWER_SPECTRUM = 9; 793 HISTOGRAM = 10; 794 LUT = 11; 795 XYZ = 12; 796 LAB = 13; 797 CMC = 14; 798 CMYK = 15; 799 LABQ = 16; 800 RGB = 17; 801 UCS = 18; 802 LCH = 19; 803 LABS = 21; 804 sRGB = 22; 805 YXY = 23; 806 FOURIER = 24; 807 RGB16 = 25; 808 GREY16 = 26; 809 810 /* Table to get names <-> numbers. 811 */ 812 type_names = Enum [ 813 $MULTIBAND => MULTIBAND, 814 $B_W => B_W, 815 $LUMINANCE => LUMINANCE, 816 $XRAY => XRAY, 817 $IR => IR, 818 $YUV => YUV, 819 $RED_ONLY => RED_ONLY, 820 $GREEN_ONLY => GREEN_ONLY, 821 $BLUE_ONLY => BLUE_ONLY, 822 $POWER_SPECTRUM => POWER_SPECTRUM, 823 $HISTOGRAM => HISTOGRAM, 824 $LUT => LUT, 825 $XYZ => XYZ, 826 $LAB => LAB, 827 $CMC => CMC, 828 $CMYK => CMYK, 829 $LABQ => LABQ, 830 $RGB => RGB, 831 $UCS => UCS, 832 $LCH => LCH, 833 $LABS => LABS, 834 $sRGB => sRGB, 835 $YXY => YXY, 836 $FOURIER => FOURIER, 837 $RGB16 => RGB16, 838 $GREY16 => GREY16 839 ]; 840 841 /* Table relating nip's colour space names and VIPS's Type numbers. 842 * Options generated from this, so match the order to the order in the 843 * Colour menu. 844 */ 845 colour_spaces = Enum [ 846 $sRGB => sRGB, 847 $Lab => LAB, 848 $LCh => LCH, 849 $XYZ => XYZ, 850 $Yxy => YXY, 851 $UCS => UCS 852 ]; 853 854 /* A slightly larger table ... the types of colorimetric image we can 855 * have. Add mono, and the S and Q forms of LAB. 856 */ 857 image_colour_spaces = Enum [ 858 $Mono => B_W, 859 $sRGB => sRGB, 860 $RGB16 => RGB16, 861 $GREY16 => GREY16, 862 $Lab => LAB, 863 $LabQ => LABQ, 864 $LabS => LABS, 865 $LCh => LCH, 866 $XYZ => XYZ, 867 $Yxy => YXY, 868 $UCS => UCS 869 ]; 870} 871 872/* Base image type. Simple layer over vips_image. 873 */ 874Image value = class 875 _Object { 876 _check_args = [ 877 [value, "value", check_image] 878 ] ++ super._check_args; 879 880 // fields from VIPS header 881 width = get_width value; 882 height = get_height value; 883 bands = get_bands value; 884 format = get_format value; 885 bits = get_bits value; 886 coding = get_coding value; 887 type = get_type value; 888 xres = get_header "Xres" value; 889 yres = get_header "Yres" value; 890 xoffset = get_header "Xoffset" value; 891 yoffset = get_header "Yoffset" value; 892 filename = get_header "filename" value; 893 894 // convenience ... the area our pixels occupy, as a rect 895 rect = Rect 0 0 width height; 896 897 // operator overloading 898 // (op Image Vector) done in Vector class 899 oo_binary_table op x = [ 900 // handle image ++ constant here 901 [wrap join_result_image, 902 (has_real x || is_Vector x) && 903 (op.op_name == "join" || op.op_name == "join'")], 904 // image ++ image is slightly different ... we want to 905 // sizealike, but we must not bandalike 906 [wrap 907 (op.fn (get_image resized?0) (get_image resized?1)), 908 has_image x && 909 (op.op_name == "join" || op.op_name == "join'")], 910 [wrap ite_result_image, 911 op.op_name == "if_then_else"], 912 // arithmetic and reational binops between image resize 913 // and band_alike images to match 914 [wrap 915 (op.fn (get_image rebanded?0) (get_image rebanded?1)), 916 has_image x && 917 (op.type == Operator_type.ARITHMETIC || 918 op.type == Operator_type.RELATIONAL)], 919 // other op types don't resize 920 [wrap (op.fn this.value (get_image x)), 921 has_image x], 922 [wrap (op.fn this.value (get_number x)), 923 has_number x], 924 // if it's not a class on the RHS, handle here ... just apply and 925 // rewrap 926 [wrap (op.fn this.value x), 927 !is_class x] 928 // all other cases handled by other classes 929 ] ++ super.oo_binary_table op x 930 { 931 // wrap the result with this 932 // x can be a non-image, eg. compare "Image v == []" vs. "Image v == 933 // 12" 934 wrap x 935 = x, op.type == Operator_type.COMPOUND || 936 !is_image x 937 = this.Image x; 938 939 join_result_image 940 = value ++ new_stuff, op.op_name == "join" 941 = new_stuff ++ value 942 { 943 new_stuff = image_new width height new_bands 944 format 945 coding 946 Image_type.B_W x xoffset yoffset; 947 new_bands 948 = get_bands x, has_bands x 949 = 1; 950 } 951 952 [then_part, else_part] = x; 953 954 // get things about our output from inputs in this order 955 objects = [then_part, else_part, this]; 956 957 // properties of our output image 958 target_bands = get_member_list has_bands get_bands objects; 959 target_type = get_member_list has_type get_type objects; 960 961 // if one of then/else is an image, get the target format from that 962 // otherwise, let the non-image objects set the target 963 target_format 964 = get_member_list has_format get_format x, 965 has_member_list has_format x 966 = []; 967 968 to_image x 969 = x, is_image x 970 = x.value, is_Image x 971 = clip2fmt target_format im, target_format != [] 972 = im 973 { 974 im = im_black width height target_bands + x; 975 } 976 977 [if_size, then_size, else_size] = 978 size_alike (value : formats_alike (map to_image x)); 979 980 ite_result_image = image_set_type target_type 981 (if if_size then then_size else else_size); 982 983 resized = size_alike [this, x]; 984 rebanded = bands_alike resized; 985 } 986 987 // FIXME ... yuk ... don't use operator hints, just always rewrap if 988 // we have an image result 989 // forced on us by things like abs: 990 // abs Vector -> real 991 // abs Image -> Image 992 // does not fit well with COMPOUND/whatever scheme 993 oo_unary_table op = [ 994 [this.Image result, 995 is_image result], 996 [result, 997 true] 998 ] ++ super.oo_unary_table op 999 { 1000 result = op.fn this.value; 1001 } 1002} 1003 1004/* Construct an image from a file. 1005 */ 1006Image_file filename = class 1007 Image value { 1008 _check_args = [ 1009 [filename, "filename", check_string] 1010 ] ++ super._check_args; 1011 1012 value = vips_image filename; 1013} 1014 1015Region image left top width height = class 1016 Image value { 1017 _check_args = [ 1018 [image, "Image", check_Image], 1019 [left, "left", check_real], 1020 [top, "top", check_real], 1021 [width, "width", check_preal], 1022 [height, "height", check_preal] 1023 ] ++ super._check_args; 1024 1025 // a rect for our coordinates 1026 // region.rect gets the rect for the extracted image 1027 region_rect = Rect left top width height; 1028 1029 // we need to always succeed ... value is our enclosing image if we're 1030 // out of bounds 1031 value 1032 = extract_area left top width height image.value, 1033 image.rect.includes_rect region_rect 1034 = image.value; 1035} 1036 1037Area image left top width height = class 1038 scope.Region image left top width height { 1039 Region image left top width height 1040 = this.Area image left top width height; 1041} 1042 1043Arrow image left top width height = class 1044 scope.Rect left top width height { 1045 _check_args = [ 1046 [image, "Image", check_Image], 1047 [left, "left", check_real], 1048 [top, "top", check_real], 1049 [width, "width", check_real], 1050 [height, "height", check_real] 1051 ] ++ super._check_args; 1052 1053 Rect l t w h = this.Arrow image l t w h; 1054} 1055 1056HGuide image top = class 1057 scope.Arrow image image.rect.left top image.width 0 { 1058 Arrow image left top width height = this.HGuide image top; 1059} 1060 1061VGuide image left = class 1062 scope.Arrow image left image.rect.top 0 image.height { 1063 Arrow image left top width height = this.VGuide image left; 1064} 1065 1066Mark image left top = class 1067 scope.Arrow image left top 0 0 { 1068 Arrow image left top width height = this.Mark image left top; 1069} 1070 1071// convenience functions: ... specify position as [0 .. 1) 1072 1073Region_relative image u v w h 1074 = Region image 1075 (image.width * u) 1076 (image.height * v) 1077 (image.width * w) 1078 (image.height * h); 1079 1080Area_relative image u v w h 1081 = Area image 1082 (image.width * u) 1083 (image.height * v) 1084 (image.width * w) 1085 (image.height * h); 1086 1087Arrow_relative image u v w h 1088 = Arrow image 1089 (image.width * u) 1090 (image.height * v) 1091 (image.width * w) 1092 (image.height * h); 1093 1094VGuide_relative image v 1095 = VGuide image (image.height * v); 1096 1097HGuide_relative image u 1098 = HGuide image (image.width * u); 1099 1100Mark_relative image u v 1101 = Mark image 1102 (image.width * u) 1103 (image.height * v); 1104 1105Interpolate = class { 1106 NEAREST_NEIGHBOUR = 0; 1107 BILINEAR = 1; 1108 BICUBIC = 2; 1109 1110 /* Table to map interpol numbers to descriptive strings 1111 */ 1112 names = Enum [ 1113 [_ "Nearest neighbour", NEAREST_NEIGHBOUR], 1114 [_ "Bilinear", BILINEAR], 1115 [_ "Bicubic", BICUBIC] 1116 ]; 1117} 1118 1119Render_intent = class { 1120 PERCEPTUAL = 0; 1121 RELATIVE = 1; 1122 SATURATION = 2; 1123 ABSOLUTE = 3; 1124 1125 /* Table to get names <-> numbers. 1126 */ 1127 names = Enum [ 1128 [_ "Perceptual", PERCEPTUAL], 1129 [_ "Relative", RELATIVE], 1130 [_ "Saturation", SATURATION], 1131 [_ "Absolute", ABSOLUTE] 1132 ]; 1133} 1134 1135// abstract base class for toolkit menus 1136Menu = class {} 1137 1138// a "----" line in a menu 1139Menuseparator = class Menu {} 1140 1141// abstract base class for items in menus 1142Menuitem label tooltip = class Menu {} 1143 1144Menupullright label tooltip = class Menuitem label tooltip {} 1145 1146Menuaction label tooltip = class Menuitem label tooltip {} 1147 1148/* Plots. 1149 */ 1150 1151Plot_style = class { 1152 POINT = 0; 1153 LINE = 1; 1154 SPLINE = 2; 1155 BAR = 3; 1156 1157 names = Enum [ 1158 [_ "Point", POINT], 1159 [_ "Line", LINE], 1160 [_ "Spline", SPLINE], 1161 [_ "Bar", BAR] 1162 ]; 1163} 1164 1165Plot_format = class { 1166 YYYY = 0; 1167 XYYY = 1; 1168 XYXY = 2; 1169 1170 names = Enum [ 1171 [_ "YYYY", YYYY], 1172 [_ "XYYY", XYXY], 1173 [_ "XYXY", XYXY] 1174 ]; 1175} 1176 1177Plot_type = class { 1178 /* Lots of Ys (ie. multiple line plots). 1179 */ 1180 YYYY = 0; 1181 1182 /* First column of matrix is X position, others are Ys (ie. multiple XY 1183 * line plots, all with the same Xes). 1184 */ 1185 XYYY = 1; 1186 1187 /* Many independent XY plots. 1188 */ 1189 XYXY = 2; 1190} 1191 1192/* "options" is a list of ["key", value] pairs. 1193 */ 1194Plot options value = class 1195 scope.Image value { 1196 Image value = this.Plot options value; 1197} 1198 1199Plot_matrix options value = class 1200 Plot options (to_image value).value { 1201} 1202 1203Plot_histogram value = class 1204 scope.Plot [] value { 1205} 1206 1207Plot_xy value = class 1208 scope.Plot [$format => Plot_format.XYYY] value { 1209} 1210 1211/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate 1212 * empty slots, for example. 1213 */ 1214NULL = class 1215 _Object { 1216 oo_binary_table op x = [ 1217 // the only operation we allow is equality .. use pointer equality, 1218 // this lets us test a == NULL and a != NULL 1219 [this === x, 1220 op.type == Operator_type.RELATIONAL && 1221 op.op_name == "equal"], 1222 [this !== x, 1223 op.type == Operator_type.RELATIONAL && 1224 op.op_name == "not_equal"] 1225 ] ++ super.oo_binary_table op x; 1226} 1227