aboutsummaryrefslogtreecommitdiff
path: root/pkg/images/tv/display/t_display.x
blob: f4156f399b1eedb3606f4d1e228734e28be3f8c4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.

include <mach.h>
include <imset.h>
include <imhdr.h>
include	<error.h>
include	<pmset.h>
include	"display.h"
include	"gwindow.h"
include	"iis.h"

# DISPLAY - Display an image.  The specified image section is mapped into
# the specified section of an image display frame.  The mapping involves
# a linear transformation in X and Y and a linear or logarithmic transformation
# in Z (greyscale).  Images of all pixel datatypes are supported, and there
# no upper limit on the size of an image.  The display device is interfaced
# to FIO as a file and is accessed herein via IMIO as just another imagefile.
# The physical characteristics of the display (i.e., X, Y, and Z resolution)
# are taken from the image header.  The display frame buffer is the pixel
# storage "file".

procedure t_display()

char	image[SZ_FNAME]		# Image to display
int	frame			# Display frame
int	erase			# Erase frame?

int	i
pointer	sp, wdes, im, ds

bool	clgetb()
int	clgeti(), btoi(), imd_wcsver(), imtlen(), imtgetim()
pointer	immap(), imd_mapframe1(), imtopenp()
errchk	immap, imd_mapframe1
errchk	ds_getparams, ds_setwcs, ds_load_display, ds_erase_border

begin
	call smark (sp)
	call salloc (wdes, LEN_WDES, TY_STRUCT)
	call aclri (Memi[wdes], LEN_WDES)

	# Open input imagefile.
	im = imtopenp ("image")
	if (imtlen (im) != 1)
	    call error (1, "Only one image may be displayed")
	i = imtgetim (im, image, SZ_FNAME)
	call imtclose (im)
	#call clgstr ("image", image, SZ_FNAME)
	im = immap (image, READ_ONLY, 0)
	if (IM_NDIM(im) <= 0)
	    call error (1, "image has no pixels")

        # Query server to get the WCS version, this also tells us whether
	# we can use the all 16 supported frames.
        if (imd_wcsver() == 0)
	    call clputi ("display.frame.p_max", 4)
	else
	    call clputi ("display.frame.p_max", 16)


	# Open display device as an image.
	frame = clgeti ("frame")
	W_FRAME(wdes)  = frame

	erase = btoi (clgetb ("erase"))
	if (erase == YES)
	    ds = imd_mapframe1 (frame, WRITE_ONLY,
		btoi (clgetb ("select_frame")), erase)
	else
	    ds = imd_mapframe1 (frame, READ_WRITE,
		btoi (clgetb ("select_frame")), erase)

	# Get display parameters and set up transformation.
	call ds_getparams (im, ds, wdes)

	# Compute and output the screen to image pixel WCS.
	call ds_setwcs (im, ds, wdes, image, frame)

	# Display the image and zero the border if necessary.
	call ds_load_display (im, ds, wdes)
	if (!clgetb ("erase") && clgetb ("border_erase"))
	    call ds_erase_border (im, ds, wdes)

	# Free storage.
	call maskcolor_free (W_OCOLORS(wdes))
	call maskcolor_free (W_BPCOLORS(wdes))
	do i = 0, W_MAXWC
	    if (W_UPTR(W_WC(wdes,i)) != NULL)
		call ds_ulutfree (W_UPTR(W_WC(wdes,i)))
	call imunmap (ds)
	call imunmap (im)

	call sfree (sp)
end


# DS_GETPARAMS -- Get the parameters controlling how the image is mapped
# into the display frame.  Set up the transformations and save in the graphics
# descriptor file.  If "repeat" mode is enabled, read the graphics descriptor
# file and reuse the transformations therein.

procedure ds_getparams (im, ds, wdes)

pointer	im, ds, wdes		#I Image, display, and graphics descriptors

bool	fill, zscale_flag, zrange_flag, zmap_flag
real	xcenter, ycenter, xsize, ysize
real	xmag, ymag, xscale, yscale, pxsize, pysize
real	z1, z2, contrast
int	nsample, ncols, nlines
pointer	wnwin, wdwin, wwwin, wipix, wdpix, zpm, bpm
pointer	sp, str, ztrans, lutfile

int	clgeti(), clgwrd(), nowhite()
real	clgetr()
pointer	maskcolor_map(), ds_pmmap(), zsc_pmsection()
pointer	ds_ulutalloc()
bool	streq(), clgetb()
errchk	maskcolor_map, ds_pmmap, zsc_pmsection, mzscale

begin
	call smark (sp)
	call salloc (str, SZ_LINE, TY_CHAR)
	call salloc (ztrans, SZ_FNAME, TY_CHAR)

	# Get overlay mask and colors.
	call clgstr ("overlay", W_OVRLY(wdes), W_SZSTRING)
	call clgstr ("ocolors", Memc[str], SZ_LINE)
	W_OCOLORS(wdes) = maskcolor_map (Memc[str])

	# Get bad pixel mask.
	call clgstr ("bpmask", W_BPM(wdes), W_SZSTRING)
	W_BPDISP(wdes) = clgwrd ("bpdisplay", Memc[str], SZ_LINE, BPDISPLAY)
	call clgstr ("bpcolors", Memc[str], SZ_LINE)
	W_BPCOLORS(wdes) = maskcolor_map (Memc[str])

	# Determine the display window into which the image is to be mapped
	# in normalized device coordinates.

	xcenter = max(0.0, min(1.0, clgetr ("xcenter")))
	ycenter = max(0.0, min(1.0, clgetr ("ycenter")))
	xsize   = max(0.0, min(1.0, clgetr ("xsize")))
	ysize   = max(0.0, min(1.0, clgetr ("ysize")))

	# Set up a new graphics descriptor structure defining the coordinate
	# transformation used to map the image into the display frame.

	wnwin = W_WC(wdes,W_NWIN)
	wdwin = W_WC(wdes,W_DWIN)
	wwwin = W_WC(wdes,W_WWIN)
	wipix = W_WC(wdes,W_IPIX)
	wdpix = W_WC(wdes,W_DPIX)

	# Determine X and Y scaling ratios required to map the image into the
	# normalized display window.  If spatial scaling is not desired filling
	# must be disabled and XMAG and YMAG must be set to 1.0 in the
	# parameter file.  Fill mode will always produce an aspect ratio of 1;
	# if nonequal scaling is required then the magnification ratios must
	# be set explicitly by the user.

	fill = clgetb ("fill")
	ncols  = IM_LEN(im,1)
	nlines = IM_LEN(im,2)
	if (fill) {
	    # Compute scale in units of window coords per data pixel required
	    # to scale image to fit window.

	    xmag = (IM_LEN(ds,1) * xsize) / ncols
	    ymag = (IM_LEN(ds,2) * ysize) / nlines

	    if (xmag > ymag)
		xmag = ymag
	    else
		ymag = xmag

	} else {
	    # Compute scale required to provide image magnification ratios
	    # specified by the user.  Magnification is specified in units of
	    # display pixels, i.e, a magnification ratio of 1.0 means that
	    # image pixels will map to display pixels without scaling.

	    xmag = clgetr ("xmag")
	    ymag = clgetr ("ymag")
	}

	xscale = 1.0 / (IM_LEN(ds,1) / xmag)
	yscale = 1.0 / (IM_LEN(ds,2) / ymag)

	# Set device window limits in normalized device coordinates.
	# World coord system 0 is used for the device window.

	W_XS(wnwin) = xcenter - xsize / 2.0
	W_XE(wnwin) = xcenter + xsize / 2.0
	W_YS(wnwin) = ycenter - ysize / 2.0
	W_YE(wnwin) = ycenter + ysize / 2.0

	# Set pixel coordinates of window.
	# If the image is too large to fit in the window given the scaling
	# factors XSCALE and YSCALE, the following will set starting and ending
	# pixel coordinates in the interior of the image.  If the image is too
	# small to fill the window then the pixel coords will reference beyond
	# the bounds of the image.  Note that the 0.5 is because NDC has
	# the screen corner at 0 while screen pixels have the corner at 0.5.

	pxsize = xsize / xscale
	pysize = ysize / yscale

	W_XS(wdwin) = (ncols / 2.0) - (pxsize / 2.0) + 0.5
	W_XE(wdwin) = W_XS(wdwin) + pxsize
	W_YS(wdwin) = (nlines / 2.0) - (pysize / 2.0) + 0.5
	W_YE(wdwin) = W_YS(wdwin) + pysize

	# Compute X and Y magnification ratios required to map image into
	# the device window in device pixel units.

	xmag = (W_XE(wnwin)-W_XS(wnwin))*IM_LEN(ds,1)/(W_XE(wdwin)-W_XS(wdwin))
	ymag = (W_YE(wnwin)-W_YS(wnwin))*IM_LEN(ds,2)/(W_YE(wdwin)-W_YS(wdwin))

	# Compute the coordinates of the image section to be displayed.
	# Round down if upper pixel is exactly at one-half.

	W_XS(wipix) = max (1, nint(W_XS(wdwin)))
	W_XE(wipix) = min (ncols, nint(W_XE(wdwin)-1.01))
	W_YS(wipix) = max (1, nint(W_YS(wdwin)))
	W_YE(wipix) = min (nlines, nint(W_YE(wdwin)-1.01))

	# Now compute the image and display pixels to be used.
	# The image may be truncated to fit in the display window.
	# These are integer coordinates at the pixel centers.

	pxsize = W_XE(wipix) - W_XS(wipix) + 1
	pysize = W_YE(wipix) - W_YS(wipix) + 1
	xcenter = (W_XE(wnwin) + W_XS(wnwin)) / 2.0 * IM_LEN(ds,1) + 0.5
	ycenter = (W_YE(wnwin) + W_YS(wnwin)) / 2.0 * IM_LEN(ds,2) + 0.5

	#W_XS(wdpix) = max (1, nint (xcenter - (pxsize/2.0*xmag) + 0.5))
	W_XS(wdpix) = max (1, int (xcenter - (pxsize/2.0*xmag) + 0.5))
	W_XE(wdpix) = min (IM_LEN(ds,1), nint (W_XS(wdpix)+pxsize*xmag - 1.01))
	#W_YS(wdpix) = max (1, nint (ycenter - (pysize/2.0*ymag) + 0.5))
	W_YS(wdpix) = max (1, int (ycenter - (pysize/2.0*ymag) + 0.5))
	W_YE(wdpix) = min (IM_LEN(ds,2), nint (W_YS(wdpix)+pysize*ymag - 1.01))

	# Now adjust the display window to be consistent with the image and
	# display pixels to be used.

	W_XS(wdwin) = W_XS(wnwin) * IM_LEN(ds,1) + 0.5
	W_XE(wdwin) = W_XE(wnwin) * IM_LEN(ds,1) + 0.5
	W_YS(wdwin) = W_YS(wnwin) * IM_LEN(ds,2) + 0.5
	W_YE(wdwin) = W_YE(wnwin) * IM_LEN(ds,2) + 0.5
	W_XS(wdwin) = (W_XS(wipix)-0.5) + (W_XS(wdwin)-(W_XS(wdpix)-0.5))/xmag
	W_XE(wdwin) = (W_XS(wipix)-0.5) + (W_XE(wdwin)-(W_XS(wdpix)-0.5))/xmag
	W_YS(wdwin) = (W_YS(wipix)-0.5) + (W_YS(wdwin)-(W_YS(wdpix)-0.5))/ymag
	W_YE(wdwin) = (W_YS(wipix)-0.5) + (W_YE(wdwin)-(W_YS(wdpix)-0.5))/ymag

	# Order of interpolator used for spatial transformation.
	W_XT(wdwin) = max(0, min(1, clgeti ("order")))
	W_YT(wdwin) = W_XT(wdwin)

	# Determine the greyscale transformation.
	call clgstr ("ztrans", Memc[ztrans], SZ_FNAME)
	if (streq (Memc[ztrans], "log"))
	    W_ZT(wdwin) = W_LOG
	else if (streq (Memc[ztrans], "linear"))
	    W_ZT(wdwin) = W_LINEAR
	else if (streq (Memc[ztrans], "none"))
	    W_ZT(wdwin) = W_UNITARY
	else if (streq (Memc[ztrans], "user")) {
	    W_ZT(wdwin) = W_USER
	    call salloc (lutfile, SZ_FNAME, TY_CHAR)
	    call clgstr ("lutfile", Memc[lutfile], SZ_FNAME)
	    W_UPTR(wdwin) = ds_ulutalloc (Memc[lutfile], z1, z2)
	} else {
	    call eprintf ("Bad greylevel transformation '%s'\n")
		call pargstr (Memc[ztrans])
	    W_ZT(wdwin) = W_LINEAR
	}

	# The zscale, and zrange parameters determine the algorithms for
	# determining Z1 and Z2, the range of input z values to be mapped
	# into the fixed range of display greylevels.  If sampling and no
	# sample mask is given then create one as a subsampled image section.
	# If greyscale mapping is disabled the zscale and zrange options are
	# disabled.  Greyscale mapping can also be disabled by turning off
	# zscale and zrange and setting Z1 and Z2 to the device greyscale min
	# and max values, producing a unitary transformation.

	if (W_ZT(wdwin) == W_UNITARY || W_ZT(wdwin) == W_USER) {
	    zscale_flag = false
	    zrange_flag = false
	    zmap_flag   = false
	} else {
	    zmap_flag = true
	    zscale_flag = clgetb ("zscale")
	    if (!zscale_flag)
		zrange_flag = clgetb ("zrange")
	}

	if (zscale_flag || (zrange_flag && IM_LIMTIME(im) < IM_MTIME(im))) {
	    call clgstr ("zmask", W_ZPM(wdes), W_SZSTRING)
	    nsample = max (100, clgeti ("nsample"))
	    if (nowhite (W_ZPM(wdes), W_ZPM(wdes), W_SZSTRING) > 0) {
		if (W_ZPM(wdes) == '[')
		    zpm = zsc_pmsection (W_ZPM(wdes), im)
		else
		    zpm = ds_pmmap (W_ZPM(wdes), im)
	    } else
		zpm = NULL
	    iferr (bpm = ds_pmmap (W_BPM(wdes), im)) {
		call erract (EA_WARN)
		bpm = NULL
	    }
	}

	if (zscale_flag) {
	    # Autoscaling is desired.  Compute Z1 and Z2 which straddle the
	    # median computed by sampling a portion of the image.

	    contrast = clgetr ("contrast")
	    call mzscale (im, zpm, bpm, contrast, nsample, z1, z2)
	    if (zpm != NULL)
		call imunmap (zpm)
	    if (bpm != NULL)
		call imunmap (bpm)

	} else if (zrange_flag) {
	    # Use the limits in the header if current otherwise get the
	    # minimum and maximum of the sample mask.
	    if (IM_LIMTIME(im) >= IM_MTIME(im)) {
		z1 = IM_MIN(im)
		z2 = IM_MAX(im)
	    } else {
		call mzscale (im, zpm, bpm, 0., nsample, z1, z2)
		if (zpm != NULL)
		    call imunmap (zpm)
		if (bpm != NULL)
		    call imunmap (bpm)
	    }

	} else if (zmap_flag) {
	    z1 = clgetr ("z1")
	    z2 = clgetr ("z2")
	} else {
	    z1 = IM_MIN(ds)
	    z2 = IM_MAX(ds)
	}

	W_ZS(wdwin) = z1
	W_ZE(wdwin) = z2

	call printf ("z1=%g z2=%g\n")
	    call pargr (z1)
	    call pargr (z2)
	call flush (STDOUT)

	# The user world coordinate system should be set from the CTRAN
	# structure in the image header, but for now we just make it equal
	# to the pixel coordinate system.

	call amovi (Memi[wdwin], Memi[wwwin], LEN_WC)
	W_UPTR(wwwin) = NULL		# should not copy pointers!!
	call sfree (sp)
end


# DS_SETWCS -- Compute the rotation matrix needed to convert screen coordinates
# (zero indexed, y-flipped) to image pixel coordinates, allowing both for the
# transformation from screen space to the image section being displayed, and
# from the image section to the physical input image.
#
# NOTE -- This code assumes that the display device is zero-indexed and
# y-flipped; this is usually the case, but should be parameterized in the
# graphcap.  This code also assumes that the full device screen is being used,
# and that we are not assigning multiple WCS to different regions of the screen.

procedure ds_setwcs (im, ds, wdes, image, frame)

pointer	im, ds, wdes		# image, display, and coordinate descriptors
char	image[SZ_FNAME]		# image section name
int	frame			# frame

real	a, b, c, d, tx, ty
int	ip, i, j, axis[2]
real	sx, sy
int	dx, dy, snx, sny, dnx, dny
pointer	sp, imname, title, wnwin, wdwin
pointer	src, dest, region, objref
long	lv[IM_MAXDIM], pv1[IM_MAXDIM], pv2[IM_MAXDIM]

bool	streq()

begin
	call smark (sp)
	call salloc (imname, SZ_FNAME, TY_CHAR)
	call salloc (title,  SZ_LINE,  TY_CHAR)
        call salloc (region, SZ_FNAME, TY_CHAR)
        call salloc (objref, SZ_FNAME, TY_CHAR)

	# Compute the rotation matrix needed to transform screen pixel coords
	# to image section coords.

	wnwin = W_WC(wdes,W_NWIN)
	wdwin = W_WC(wdes,W_DWIN)

	# X transformation.
	a  = (W_XE(wdwin)-W_XS(wdwin))/((W_XE(wnwin)-W_XS(wnwin))*IM_LEN(ds,1))
	c  = 0.0			# not rotated, cross term is zero
	tx = W_XS(wdwin) - a * (W_XS(wnwin) * IM_LEN(ds,1))

	# Y transformation.
	b  = 0.0			# not rotated, cross term is zero
	d  = (W_YE(wdwin)-W_YS(wdwin))/((W_YE(wnwin)-W_YS(wnwin))*IM_LEN(ds,2))
	ty = W_YS(wdwin) - d * (W_YS(wnwin) * IM_LEN(ds,2))

	# Now allow for the Y-flip (origin at upper left in display window).
	d = -d
	ty = W_YE(wdwin) - d * ((1.0 - W_YE(wnwin)) * IM_LEN(ds,2))

	# Now translate the screen corner to the center of the screen pixel.
	tx = tx + 0.5 * a
	ty = ty + 0.5 * d

	# Determine the logical to physical mapping by evaluating two points.
	# and determining the axis reduction if any.  pv1 will be the
	# offset and pv2-pv1 will be the scale.

	call aclrl (pv1, IM_MAXDIM)
	call aclrl (lv, IM_MAXDIM)
	call imaplv (im, lv, pv1, 2)
	call amovkl (long(1), lv, IM_MAXDIM)
	call aclrl (pv2, IM_MAXDIM)
	call imaplv (im, lv, pv2, 2)

	i = 1
	axis[1] = 1;  axis[2] = 2
	do j = 1, IM_MAXDIM
	    if (pv1[j] != pv2[j]) {
		axis[i] = j
		i = i + 1
	    }

	pv2[axis[1]] = (pv2[axis[1]] - pv1[axis[1]])
	pv2[axis[2]] = (pv2[axis[2]] - pv1[axis[2]])

	# These imply a new rotation matrix which we won't bother to work out
	# separately here.  Multiply the two rotation matrices and add the
	# translation vectors to get the overall transformation from screen
	# coordinates to image coordinates.
	a = a * pv2[axis[1]]
	d = d * pv2[axis[2]]
	tx = tx * pv2[axis[1]] + pv1[axis[1]]
	ty = ty * pv2[axis[2]] + pv1[axis[2]]

	# Get the image name (minus image section) and
	# title string (minus any newline.
	call ds_gimage (im, image, Memc[imname], SZ_FNAME)
	call strcpy (IM_TITLE(im), Memc[title], SZ_LINE)
	for (ip=title;  Memc[ip] != '\n' && Memc[ip] != EOS;  ip=ip+1)
	    ;
	Memc[ip] = EOS


	# Define the mapping from the image pixels to frame buffer pixels.
        src  = W_WC(wdes,W_IPIX)
	sx   = W_XS(src)
	sy   = W_YS(src)
	snx  = (W_XE(src) - W_XS(src) + 1)
	sny  = (W_YE(src) - W_YS(src) + 1)

        dest = W_WC(wdes,W_DPIX)
	dx   = W_XS(dest)
	dy   = W_YS(dest)
	dnx  = (W_XE(dest) - W_XS(dest) + 1)
	dny  = (W_YE(dest) - W_YS(dest) + 1)

	# For a single image display the 'region' is fixed. The object ref
	# is the fully defined image node!prefix path, including any sections.
        # We need a special kludge to keep backward compatability with the
        # use of "dev$pix" as the standard test image name.
        call strcpy ("image", Memc[region], SZ_FNAME)
        if (streq (image, "dev$pix"))
            call fpathname ("dev$pix.imh", Memc[objref], SZ_PATHNAME)
        else
            call fpathname (image, Memc[objref], SZ_PATHNAME)

	# Add the mapping info to be written with the WCS.
	call imd_setmapping (Memc[region], sx, sy, snx, sny,
	    dx, dy, dnx, dny, Memc[objref])

	# Write the WCS.
	call imd_putwcs (ds, frame, Memc[imname], Memc[title],
	    a, b, c, d, tx, ty, W_ZS(wdwin), W_ZE(wdwin), W_ZT(wdwin))

	call sfree (sp)
end


# DS_GIMAGE -- Convert input image section name to a 2D physical image section.

procedure ds_gimage (im, input, output, maxchar)

pointer	im			#I IMIO pointer
char	input[ARB]		#I Input image name
char	output[maxchar]		#O Output image name
int	maxchar			#I Maximum characters in output name.

int	i, fd
pointer	sp, section, lv, pv1, pv2

int	stropen(), strlen()
bool	streq()

begin
	call smark (sp)
	call salloc (section, SZ_FNAME, TY_CHAR)
	call salloc (lv, IM_MAXDIM, TY_LONG)
	call salloc (pv1, IM_MAXDIM, TY_LONG)
	call salloc (pv2, IM_MAXDIM, TY_LONG)

	# Get endpoint coordinates in original image.
	call amovkl (long(1), Meml[lv], IM_MAXDIM)
	call aclrl (Meml[pv1], IM_MAXDIM)
	call imaplv (im, Meml[lv], Meml[pv1], 2)
	call amovl (IM_LEN(im,1), Meml[lv], IM_NDIM(im))
	call aclrl (Meml[pv2], IM_MAXDIM)
	call imaplv (im, Meml[lv], Meml[pv2], 2)

	# Set image section.
	fd = stropen (Memc[section], SZ_FNAME, NEW_FILE)
	call fprintf (fd, "[")
	do i = 1, IM_MAXDIM {
	    if (Meml[pv1+i-1] != Meml[pv2+i-1])
		call fprintf (fd, "*")
	    else if (Meml[pv1+i-1] != 0) {
		call fprintf (fd, "%d")
		call pargi (Meml[pv1+i-1])
	    } else
		break
	    call fprintf (fd, ",")
	}
	call close (fd)
	i = strlen (Memc[section])
	Memc[section+i-1] = ']'

	if (streq ("[*,*]", Memc[section]))
	    Memc[section] = EOS

	# Strip existing image section and add new section.
#	call imgimage (input, output, maxchar)
#	call strcat (Memc[section], output, maxchar)
	
	if (Memc[section] == EOS)
	    call imgimage (input, output, maxchar)
	else
	    call strcpy (input, output, maxchar)

	call sfree (sp)
end


# DS_LOAD_DISPLAY -- Map an image into the display window.  In general this
#   involves independent linear transformations in the X, Y, and Z (greyscale)
#   dimensions.  If a spatial dimension is larger than the display window then
#   the image is block averaged.  If a spatial dimension or a block averaged
#   dimension is smaller than the display window then linear interpolation is
#   used to expand the image.  Both the input image and the output device appear
#   to us as images, accessed via IMIO.  All spatial scaling is 
#   handled by the "scaled input" package, i.e., SIGM2[SR].  Our task is to
#   get lines from the scaled input image, transform the greyscale if necessary,
#   and write the lines to the output device.

procedure ds_load_display (im, ds, wdes)

pointer	im			# input image
pointer	ds			# output image
pointer	wdes			# graphics window descriptor

real	z1, z2, dz1, dz2, px1, px2, py1, py2
int	i, order, zt, wx1, wx2, wy1, wy2, wy, nx, ny, xblk, yblk, color
pointer	wdwin, wipix, wdpix, ovrly, bpm, pm, uptr
pointer	in, out, si, si_ovrly, si_bpovrly, ocolors, bpcolors, rtemp
bool	unitary_greyscale_transformation
short	lut1, lut2, dz1_s, dz2_s, z1_s, z2_s

bool	fp_equalr()
int	imstati(), maskcolor()
pointer	ds_pmmap(), imps2s(), imps2r()
pointer	sigm2s(), sigm2i(), sigm2r(), sigm2_setup()
errchk	ds_pmmap, imps2s, imps2r, sigm2s, sigm2i, sigm2r, sigm2_setup
errchk	maskexprn

begin
	wdwin = W_WC(wdes,W_DWIN)
	wipix = W_WC(wdes,W_IPIX)
	wdpix = W_WC(wdes,W_DPIX)

	# Set image and display pixels.
	px1 = nint (W_XS(wipix))
	px2 = nint (W_XE(wipix))
	py1 = nint (W_YS(wipix))
	py2 = nint (W_YE(wipix))
	wx1 = nint (W_XS(wdpix))
	wx2 = nint (W_XE(wdpix))
	wy1 = nint (W_YS(wdpix))
	wy2 = nint (W_YE(wdpix))

	z1 = W_ZS(wdwin)
	z2 = W_ZE(wdwin)
	zt = W_ZT(wdwin)
	uptr = W_UPTR(wdwin)
	order = max (W_XT(wdwin), W_YT(wdwin))

	# Setup scaled input and masks.
	si = NULL
	si_ovrly = NULL
	si_bpovrly = NULL
	nx = wx2 - wx1 + 1
	ny = wy2 - wy1 + 1
	xblk = INDEFI
	yblk = INDEFI

	ocolors = W_OCOLORS(wdes)
	iferr (ovrly = ds_pmmap (W_OVRLY(wdes), im)) {
	    call erract (EA_WARN)
	    ovrly = NULL
	}
	if (ovrly != NULL) {
	    xblk = INDEFI
	    yblk = INDEFI
	    si_ovrly = sigm2_setup (ovrly, NULL, px1,px2,nx,xblk,
		py1,py2,ny,yblk, -1)
	}

	bpcolors = W_BPCOLORS(wdes)
	switch (W_BPDISP(wdes)) {
	case BPDNONE:
	    si = sigm2_setup (im, NULL, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
	case BPDOVRLY:
	    si = sigm2_setup (im, NULL, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
	    iferr (bpm = ds_pmmap (W_BPM(wdes), im))
		bpm = NULL
	    if (bpm != NULL)
		si_bpovrly = sigm2_setup (bpm, NULL, px1,px2,nx,xblk,
		    py1,py2,ny,yblk, -1)
	case BPDINTERP:
	    iferr (bpm = ds_pmmap (W_BPM(wdes), im))
		bpm = NULL
	    if (bpm != NULL)
		pm = imstati (bpm, IM_PMDES)
	    else
		pm = NULL
	    si = sigm2_setup (im, pm, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
	}

	# The device IM_MIN and IM_MAX parameters define the acceptable range
	# of greyscale values for the output device (e.g., 0-255 for most 8-bit
	# display devices).  Values Z1 and Z2 are mapped linearly or
	# logarithmically into IM_MIN and IM_MAX.

	dz1 = IM_MIN(ds)
	dz2 = IM_MAX(ds)
	if (fp_equalr (z1, z2)) {
	    z1 = z1 - 1
	    z2 = z2 + 1
	}

	# If the user specifies the transfer function, verify that the
	# intensity and greyscale are in range.

	if (zt == W_USER) {
	    call alims (Mems[uptr], U_MAXPTS, lut1, lut2)
	    dz1_s = short (dz1)
	    dz2_s = short (dz2)
	    if (lut2 < dz1_s || lut1 > dz2_s)
		call eprintf ("User specified greyscales out of range\n")
	    if (z2 < IM_MIN(im) || z1 > IM_MAX(im))
		call eprintf ("User specified intensities out of range\n")
	}

	# Type short pixels are treated as a special case to minimize vector
	# operations for such images (which are common).  If the image pixels
	# are either short or real then only the ALTR (greyscale transformation)
	# vector operation is required.  The ALTR operator linearly maps
	# greylevels in the range Z1:Z2 to DZ1:DZ2, and does a floor ceiling
	# of DZ1:DZ2 on all pixels outside the range.  If unity mapping is
	# employed the data is simply copied, i.e., floor ceiling constraints
	# are not applied.  This is very fast and will produce a contoured
	# image on the display which will be adequate for some applications.

	if (zt == W_UNITARY) {
	    unitary_greyscale_transformation = true
	} else if (zt == W_LINEAR) {
	    unitary_greyscale_transformation =
		(fp_equalr(z1,dz1) && fp_equalr(z2,dz2))
	} else
	    unitary_greyscale_transformation = false

	if (IM_PIXTYPE(im) == TY_SHORT && zt != W_LOG) {
	    z1_s = z1;  z2_s = z2
	    if (z1_s == z2_s) {
		z1_s = z1_s - 1
		z2_s = z2_s + 1
	    }

	    for (wy=wy1;  wy <= wy2;  wy=wy+1) {
		in  = sigm2s (si, wy - wy1 + 1)
		out = imps2s (ds, wx1, wx2, wy, wy)

		if (unitary_greyscale_transformation) {
		    call amovs (Mems[in], Mems[out], nx)
		} else if (zt == W_USER) {
		    dz1_s = U_Z1; dz2_s = U_Z2
		    call amaps (Mems[in],Mems[out],nx, z1_s,z2_s, dz1_s,dz2_s)
		    call aluts (Mems[out], Mems[out], nx, Mems[uptr])
		} else {
		    dz1_s = dz1; dz2_s = dz2
		    call amaps (Mems[in],Mems[out],nx, z1_s,z2_s, dz1_s,dz2_s)
		}

		if (si_ovrly != NULL) {
		    in = sigm2i (si_ovrly, wy - wy1 + 1)
		    call maskexprn (ocolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (ocolors, Memi[in+i])
			    if (color >= 0)
				Mems[out+i] = color
			}
		    }
		}
		if (si_bpovrly != NULL) {
		    in = sigm2i (si_bpovrly, wy - wy1 + 1)
		    call maskexprn (bpcolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (bpcolors, Memi[in+i])
			    if (color >= 0)
				Mems[out+i] = color
			}
		    }
		}
	    }

	} else if (zt == W_USER) {
	    call salloc (rtemp, nx, TY_REAL)

	    for (wy=wy1;  wy <= wy2;  wy=wy+1) {
		in  = sigm2r (si, wy - wy1 + 1)
		out = imps2s (ds, wx1, wx2, wy, wy)

		call amapr (Memr[in], Memr[rtemp], nx, z1, z2, 
		    real(U_Z1), real(U_Z2))
		call achtrs (Memr[rtemp], Mems[out], nx)
		call aluts (Mems[out], Mems[out], nx, Mems[uptr])

		if (si_ovrly != NULL) {
		    in = sigm2i (si_ovrly, wy - wy1 + 1)
		    call maskexprn (ocolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (ocolors, Memi[in+i])
			    if (color >= 0)
				Mems[out+i] = color
			}
		    }
		}
		if (si_bpovrly != NULL) {
		    in = sigm2i (si_bpovrly, wy - wy1 + 1)
		    call maskexprn (bpcolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (bpcolors, Memi[in+i])
			    if (color >= 0)
				Mems[out+i] = color
			}
		    }
		}
	    }

	} else {
	    for (wy=wy1;  wy <= wy2;  wy=wy+1) {
		in  = sigm2r (si, wy - wy1 + 1)
		out = imps2r (ds, wx1, wx2, wy, wy)

		if (unitary_greyscale_transformation)  {
		    call amovr (Memr[in], Memr[out], nx)
		} else if (zt == W_LOG) {
		    call amapr (Memr[in], Memr[out], nx,
			z1, z2, 1.0, 10.0 ** MAXLOG)
                    do i = 0, nx-1
                        Memr[out+i] = log10 (Memr[out+i])
		    call amapr (Memr[out], Memr[out], nx,
			0.0, real(MAXLOG), dz1, dz2)
		} else
		    call amapr (Memr[in], Memr[out], nx, z1, z2, dz1, dz2)

		if (si_ovrly != NULL) {
		    in = sigm2i (si_ovrly, wy - wy1 + 1)
		    call maskexprn (ocolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (ocolors, Memi[in+i])
			    if (color >= 0)
				Memr[out+i] = color
			}
		    }
		}
		if (si_bpovrly != NULL) {
		    in = sigm2i (si_bpovrly, wy - wy1 + 1)
		    call maskexprn (bpcolors, in, nx)
		    do i = 0, nx-1 {
			if (Memi[in+i] != 0) {
			    color = maskcolor (bpcolors, Memi[in+i])
			    if (color >= 0)
				Memr[out+i] = color
			}
		    }
		}
	    }
	}

	call sigm2_free (si)
	if (si_ovrly != NULL)
	    call sigm2_free (si_ovrly)
	if (si_bpovrly != NULL)
	    call sigm2_free (si_bpovrly)
	if (ovrly != NULL)
	    call imunmap (ovrly)
	if (bpm != NULL)
	    call imunmap (bpm)
end


# DS_ERASE_BORDER -- Zero the border of the window if the frame has not been
# erased, and if the displayed section does not occupy the full window.
# It would be more efficient to do this while writing the greyscale data to
# the output image, but that would complicate the display procedures and frames
# are commonly erased before displaying an image.

procedure ds_erase_border (im, ds, wdes)

pointer	im			# input image
pointer	ds			# output image (display) 
pointer	wdes			# window descriptor

int	wx1,wx2,wy1,wy2		# section of display window filled by image data
int	dx1,dx2,dy1,dy2		# coords of full display window in device pixels
int	i, nx
pointer	wdwin, wdpix
pointer	imps2s()
errchk	imps2s

begin
	wdwin = W_WC(wdes,W_DWIN)
	wdpix = W_WC(wdes,W_DPIX)

	# Set display pixels and display window pixels.
	wx1 = nint (W_XS(wdpix))
	wx2 = nint (W_XE(wdpix))
	wy1 = nint (W_YS(wdpix))
	wy2 = nint (W_YE(wdpix))
	dx1 = max (1, nint (W_XS(wdwin)))
	dx2 = min (IM_LEN(ds,1), nint (W_XE(wdwin) - 0.01))
	dy1 = max (1, nint (W_YS(wdwin)))
	dy2 = min (IM_LEN(ds,2), nint (W_YE(wdwin) - 0.01))
	nx = dx2 - dx1 + 1

	# Erase lower margin.
	for (i=dy1;  i < wy1;  i=i+1)
	    call aclrs (Mems[imps2s (ds, dx1, dx2, i, i)], nx)

	# Erase left and right margins.  By doing the right margin of a line
	# immediately after the left margin we have a high liklihood that the
	# display line will still be in the FIO buffer.

	for (i=wy1;  i <= wy2;  i=i+1) {
	    if (dx1 < wx1)
		call aclrs (Mems[imps2s (ds, dx1, wx1-1, i, i)], wx1 - dx1)
	    if (wx2 < dx2)
		call aclrs (Mems[imps2s (ds, wx2+1, dx2, i, i)], dx2 - wx2)
	}

	# Erase upper margin.
	for (i=wy2+1;  i <= dy2;  i=i+1)
	    call aclrs (Mems[imps2s (ds, dx1, dx2, i, i)], nx)
end