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 820 /* Table to get names <-> numbers. 821 */ 822 type_names = Enum [ 823 $MULTIBAND => MULTIBAND, 824 $B_W => B_W, 825 $HISTOGRAM => HISTOGRAM, 826 $XYZ => XYZ, 827 $LAB => LAB, 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 $ARRAY => ARRAY 840 ]; 841 842 /* Table relating nip's colour space names and VIPS's Type numbers. 843 * Options generated from this, so match the order to the order in the 844 * Colour menu. 845 */ 846 colour_spaces = Enum [ 847 $sRGB => sRGB, 848 $Lab => LAB, 849 $LCh => LCH, 850 $XYZ => XYZ, 851 $Yxy => YXY, 852 $UCS => UCS 853 ]; 854 855 /* A slightly larger table ... the types of colorimetric image we can 856 * have. Add mono, and the S and Q forms of LAB. 857 */ 858 image_colour_spaces = Enum [ 859 $Mono => B_W, 860 $sRGB => sRGB, 861 $RGB16 => RGB16, 862 $GREY16 => GREY16, 863 $Lab => LAB, 864 $LabQ => LABQ, 865 $LabS => LABS, 866 $LCh => LCH, 867 $XYZ => XYZ, 868 $Yxy => YXY, 869 $UCS => UCS 870 ]; 871} 872 873/* Base image type. Simple layer over vips_image. 874 */ 875Image value = class 876 _Object { 877 _check_args = [ 878 [value, "value", check_image] 879 ]; 880 881 // fields from VIPS header 882 width = get_width value; 883 height = get_height value; 884 bands = get_bands value; 885 format = get_format value; 886 bits = get_bits value; 887 coding = get_coding value; 888 type = get_type value; 889 xres = get_header "Xres" value; 890 yres = get_header "Yres" value; 891 xoffset = get_header "Xoffset" value; 892 yoffset = get_header "Yoffset" value; 893 filename = get_header "filename" value; 894 895 // convenience ... the area our pixels occupy, as a rect 896 rect = Rect 0 0 width height; 897 898 // operator overloading 899 // (op Image Vector) done in Vector class 900 oo_binary_table op x = [ 901 // handle image ++ constant here 902 [wrap join_result_image, 903 (has_real x || is_Vector x) && 904 (op.op_name == "join" || op.op_name == "join'")], 905 [wrap ite_result_image, 906 op.op_name == "if_then_else"], 907 [wrap (op.fn this.value (get_image x)), 908 has_image x], 909 [wrap (op.fn this.value (get_number x)), 910 has_number x], 911 // if it's not a class on the RHS, handle here ... just apply and 912 // rewrap 913 [wrap (op.fn this.value x), 914 !is_class x] 915 // all other cases handled by other classes 916 ] ++ super.oo_binary_table op x 917 { 918 // wrap the result with this 919 // x can be a non-image, eg. compare "Image v == []" vs. 920 // "Image v == 12" 921 wrap x 922 = x, op.type == Operator_type.COMPOUND || 923 !is_image x 924 = this.Image x; 925 926 join_result_image 927 = value ++ new_stuff, op.op_name == "join" 928 = new_stuff ++ value 929 { 930 new_stuff = image_new width height new_bands 931 format 932 coding 933 Image_type.B_W x xoffset yoffset; 934 new_bands 935 = get_bands x, has_bands x 936 = 1; 937 } 938 939 [then_part, else_part] = x; 940 941 // get things about our output from inputs in this order 942 objects = [then_part, else_part, this]; 943 944 // properties of our output image 945 target_bands = get_member_list has_bands get_bands objects; 946 target_type = get_member_list has_type get_type objects; 947 948 // if one of then/else is an image, get the target format from that 949 // otherwise, let the non-image objects set the target 950 target_format 951 = get_member_list has_format get_format x, 952 has_member_list has_format x 953 = NULL; 954 955 to_image x = to_image_size width height target_bands target_format x; 956 957 [then', else'] = map to_image x; 958 959 ite_result_image = image_set_type target_type 960 (if value then then' else else'); 961 } 962 963 // FIXME ... yuk ... don't use operator hints, just always rewrap if 964 // we have an image result 965 // forced on us by things like abs: 966 // abs Vector -> real 967 // abs Image -> Image 968 // does not fit well with COMPOUND/whatever scheme 969 oo_unary_table op = [ 970 [this.Image result, 971 is_image result], 972 [result, 973 true] 974 ] ++ super.oo_unary_table op 975 { 976 result = op.fn this.value; 977 } 978} 979 980/* Construct an image from a file. 981 */ 982Image_file filename = class 983 Image value { 984 _check_args = [ 985 [filename, "filename", check_string] 986 ]; 987 988 value = vips_image filename; 989} 990 991Region image left top width height = class 992 Image value { 993 _check_args = [ 994 [image, "Image", check_Image], 995 [left, "left", check_real], 996 [top, "top", check_real], 997 [width, "width", check_preal], 998 [height, "height", check_preal] 999 ]; 1000 1001 // a rect for our coordinates 1002 // region.rect gets the rect for the extracted image 1003 region_rect = Rect left top width height; 1004 1005 // we need to always succeed ... value is our enclosing image if we're 1006 // out of bounds 1007 value 1008 = extract_area left top width height image.value, 1009 image.rect.includes_rect region_rect 1010 = image.value; 1011} 1012 1013Area image left top width height = class 1014 scope.Region image left top width height { 1015 Region image left top width height 1016 = this.Area image left top width height; 1017} 1018 1019Arrow image left top width height = class 1020 scope.Rect left top width height { 1021 _check_args = [ 1022 [image, "Image", check_Image], 1023 [left, "left", check_real], 1024 [top, "top", check_real], 1025 [width, "width", check_real], 1026 [height, "height", check_real] 1027 ]; 1028 1029 Rect l t w h = this.Arrow image l t w h; 1030} 1031 1032HGuide image top = class 1033 scope.Arrow image image.rect.left top image.width 0 { 1034 Arrow image left top width height = this.HGuide image top; 1035} 1036 1037VGuide image left = class 1038 scope.Arrow image left image.rect.top 0 image.height { 1039 Arrow image left top width height = this.VGuide image left; 1040} 1041 1042Mark image left top = class 1043 scope.Arrow image left top 0 0 { 1044 Arrow image left top width height = this.Mark image left top; 1045} 1046 1047// convenience functions: ... specify position as [0 .. 1) 1048 1049Region_relative image u v w h 1050 = Region image 1051 (image.width * u) 1052 (image.height * v) 1053 (image.width * w) 1054 (image.height * h); 1055 1056Area_relative image u v w h 1057 = Area image 1058 (image.width * u) 1059 (image.height * v) 1060 (image.width * w) 1061 (image.height * h); 1062 1063Arrow_relative image u v w h 1064 = Arrow image 1065 (image.width * u) 1066 (image.height * v) 1067 (image.width * w) 1068 (image.height * h); 1069 1070VGuide_relative image v 1071 = VGuide image (image.height * v); 1072 1073HGuide_relative image u 1074 = HGuide image (image.width * u); 1075 1076Mark_relative image u v 1077 = Mark image 1078 (image.width * u) 1079 (image.height * v); 1080 1081Interpolate_type = class { 1082 NEAREST_NEIGHBOUR = 0; 1083 BILINEAR = 1; 1084 BICUBIC = 2; 1085 LBB = 3; 1086 NOHALO = 4; 1087 VSQBS = 5; 1088 1089 // Should introspect to get the list of interpolators :-( 1090 // We can "dir" on VipsInterpolate to get a list of them, but we 1091 // can't get i18n'd descriptions until we have more 1092 // introspection stuff in nip2. 1093 1094 /* Table to map interpol numbers to descriptive strings 1095 */ 1096 descriptions = [ 1097 _ "Nearest neighbour", 1098 _ "Bilinear", 1099 _ "Bicubic", 1100 _ "Upsize: reduced halo bicubic (LBB)", 1101 _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", 1102 _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" 1103 ]; 1104 1105 /* And to vips type names. 1106 */ 1107 types = [ 1108 "VipsInterpolateNearest", 1109 "VipsInterpolateBilinear", 1110 "VipsInterpolateBicubic", 1111 "VipsInterpolateLbb", 1112 "VipsInterpolateNohalo", 1113 "VipsInterpolateVsqbs" 1114 ]; 1115} 1116 1117Interpolate type options = class { 1118 value = vips_object_new Interpolate_type.types?type [] options; 1119} 1120 1121Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; 1122 1123Interpolate_picker default = class 1124 Interpolate interp.value [] { 1125 _vislevel = 2; 1126 1127 interp = Option "Interpolation" Interpolate_type.descriptions default; 1128} 1129 1130Render_intent = class { 1131 PERCEPTUAL = 0; 1132 RELATIVE = 1; 1133 SATURATION = 2; 1134 ABSOLUTE = 3; 1135 1136 /* Table to get names <-> numbers. 1137 */ 1138 names = Enum [ 1139 _ "Perceptual" => PERCEPTUAL, 1140 _ "Relative" => RELATIVE, 1141 _ "Saturation" => SATURATION, 1142 _ "Absolute" => ABSOLUTE 1143 ]; 1144} 1145 1146// abstract base class for toolkit menus 1147Menu = class {} 1148 1149// a "----" line in a menu 1150Menuseparator = class Menu {} 1151 1152// abstract base class for items in menus 1153Menuitem label tooltip = class Menu {} 1154 1155Menupullright label tooltip = class Menuitem label tooltip {} 1156 1157Menuaction label tooltip = class Menuitem label tooltip {} 1158 1159/* Plots. 1160 */ 1161 1162Plot_style = class { 1163 POINT = 0; 1164 LINE = 1; 1165 SPLINE = 2; 1166 BAR = 3; 1167 1168 names = Enum [ 1169 _ "Point" => POINT, 1170 _ "Line" => LINE, 1171 _ "Spline" => SPLINE, 1172 _ "Bar" => BAR 1173 ]; 1174} 1175 1176Plot_format = class { 1177 YYYY = 0; 1178 XYYY = 1; 1179 XYXY = 2; 1180 1181 names = Enum [ 1182 _ "YYYY" => YYYY, 1183 _ "XYYY" => XYXY, 1184 _ "XYXY" => XYXY 1185 ]; 1186} 1187 1188Plot_type = class { 1189 /* Lots of Ys (ie. multiple line plots). 1190 */ 1191 YYYY = 0; 1192 1193 /* First column of matrix is X position, others are Ys (ie. multiple XY 1194 * line plots, all with the same Xes). 1195 */ 1196 XYYY = 1; 1197 1198 /* Many independent XY plots. 1199 */ 1200 XYXY = 2; 1201} 1202 1203/* "options" is a list of ["key", value] pairs. 1204 */ 1205Plot options value = class 1206 scope.Image value { 1207 Image value = this.Plot options value; 1208 to_image dpi = extract_bands 0 3 1209 (graph_export_image (to_real dpi) this); 1210} 1211 1212Plot_matrix options value = class 1213 Plot options (to_image value).value { 1214} 1215 1216Plot_histogram value = class 1217 scope.Plot [] value { 1218} 1219 1220Plot_xy value = class 1221 scope.Plot [$format => Plot_format.XYYY] value { 1222} 1223 1224/* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate 1225 * empty slots, for example. 1226 */ 1227NULL = class 1228 _Object { 1229 oo_binary_table op x = [ 1230 // the only operation we allow is equality .. use pointer equality, 1231 // this lets us test a == NULL and a != NULL 1232 [this === x, 1233 op.type == Operator_type.RELATIONAL && 1234 op.op_name == "equal"], 1235 [this !== x, 1236 op.type == Operator_type.RELATIONAL && 1237 op.op_name == "not_equal"] 1238 ] ++ super.oo_binary_table op x; 1239} 1240