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