aboutsummaryrefslogtreecommitdiff
path: root/sys/plio
diff options
context:
space:
mode:
authorJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
committerJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
commit40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch)
tree4464880c571602d54f6ae114729bf62a89518057 /sys/plio
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'sys/plio')
-rw-r--r--sys/plio/PLIO.hlp1341
-rw-r--r--sys/plio/README288
-rw-r--r--sys/plio/mkpkg94
-rw-r--r--sys/plio/placcess.x59
-rw-r--r--sys/plio/plalloc.x39
-rw-r--r--sys/plio/plascii.x66
-rw-r--r--sys/plio/plbox.h10
-rw-r--r--sys/plio/plbox.x37
-rw-r--r--sys/plio/plcircle.h10
-rw-r--r--sys/plio/plcircle.x43
-rw-r--r--sys/plio/plclear.x32
-rw-r--r--sys/plio/plclose.x26
-rw-r--r--sys/plio/plcmpress.x90
-rw-r--r--sys/plio/plcompare.x35
-rw-r--r--sys/plio/plcreate.x22
-rw-r--r--sys/plio/pldbgout.x47
-rw-r--r--sys/plio/pldebug.x218
-rw-r--r--sys/plio/plempty.x25
-rw-r--r--sys/plio/plemptyline.x14
-rw-r--r--sys/plio/plglls.x39
-rw-r--r--sys/plio/plglp.gx38
-rw-r--r--sys/plio/plglr.gx44
-rw-r--r--sys/plio/plgplane.x15
-rw-r--r--sys/plio/plgsize.x26
-rw-r--r--sys/plio/pll2p.gx105
-rw-r--r--sys/plio/pll2r.gx117
-rw-r--r--sys/plio/pllen.x14
-rw-r--r--sys/plio/plleq.x44
-rw-r--r--sys/plio/plline.x66
-rw-r--r--sys/plio/pllinene.x17
-rw-r--r--sys/plio/pllnext.x61
-rw-r--r--sys/plio/plload.x83
-rw-r--r--sys/plio/plloadf.x67
-rw-r--r--sys/plio/plloadim.x99
-rw-r--r--sys/plio/plloop.x31
-rw-r--r--sys/plio/pllpr.x111
-rw-r--r--sys/plio/pllrop.x271
-rw-r--r--sys/plio/pllseg.h56
-rw-r--r--sys/plio/pllsten.x289
-rw-r--r--sys/plio/plnewcopy.x30
-rw-r--r--sys/plio/plopen.x30
-rw-r--r--sys/plio/plp2l.gx126
-rw-r--r--sys/plio/plp2r.gx71
-rw-r--r--sys/plio/plplls.x35
-rw-r--r--sys/plio/plplp.gx41
-rw-r--r--sys/plio/plplr.gx41
-rw-r--r--sys/plio/plpoint.x62
-rw-r--r--sys/plio/plpolygon.h16
-rw-r--r--sys/plio/plpolygon.x71
-rw-r--r--sys/plio/plprop.gx177
-rw-r--r--sys/plio/plr2l.gx130
-rw-r--r--sys/plio/plr2p.gx74
-rw-r--r--sys/plio/plregrop.x76
-rw-r--r--sys/plio/plreq.gx27
-rw-r--r--sys/plio/plrio.x350
-rw-r--r--sys/plio/plrop.x93
-rw-r--r--sys/plio/plrpr.gx56
-rw-r--r--sys/plio/plrrop.gx195
-rw-r--r--sys/plio/plrseg.h58
-rw-r--r--sys/plio/plsave.x86
-rw-r--r--sys/plio/plsavef.x59
-rw-r--r--sys/plio/plsaveim.x122
-rw-r--r--sys/plio/plsectnc.x113
-rw-r--r--sys/plio/plsectne.x101
-rw-r--r--sys/plio/plseti.x28
-rw-r--r--sys/plio/plsplane.x15
-rw-r--r--sys/plio/plssize.x64
-rw-r--r--sys/plio/plsslv.x25
-rw-r--r--sys/plio/plstati.x31
-rw-r--r--sys/plio/plsten.x92
-rw-r--r--sys/plio/plubox.x45
-rw-r--r--sys/plio/plucircle.x54
-rw-r--r--sys/plio/plupdate.x158
-rw-r--r--sys/plio/plupolygon.x223
-rw-r--r--sys/plio/plvalid.x22
-rw-r--r--sys/plio/tf/mkpkg51
-rw-r--r--sys/plio/tf/plglpi.x38
-rw-r--r--sys/plio/tf/plglpl.x38
-rw-r--r--sys/plio/tf/plglps.x38
-rw-r--r--sys/plio/tf/plglri.x44
-rw-r--r--sys/plio/tf/plglrl.x44
-rw-r--r--sys/plio/tf/plglrs.x44
-rw-r--r--sys/plio/tf/pll2pi.x105
-rw-r--r--sys/plio/tf/pll2pl.x105
-rw-r--r--sys/plio/tf/pll2ps.x105
-rw-r--r--sys/plio/tf/pll2ri.x117
-rw-r--r--sys/plio/tf/pll2rl.x117
-rw-r--r--sys/plio/tf/pll2rs.x117
-rw-r--r--sys/plio/tf/plp2li.x126
-rw-r--r--sys/plio/tf/plp2ll.x126
-rw-r--r--sys/plio/tf/plp2ls.x126
-rw-r--r--sys/plio/tf/plp2ri.x71
-rw-r--r--sys/plio/tf/plp2rl.x71
-rw-r--r--sys/plio/tf/plp2rs.x71
-rw-r--r--sys/plio/tf/plplpi.x41
-rw-r--r--sys/plio/tf/plplpl.x41
-rw-r--r--sys/plio/tf/plplps.x41
-rw-r--r--sys/plio/tf/plplri.x41
-rw-r--r--sys/plio/tf/plplrl.x41
-rw-r--r--sys/plio/tf/plplrs.x41
-rw-r--r--sys/plio/tf/plpropi.x177
-rw-r--r--sys/plio/tf/plpropl.x177
-rw-r--r--sys/plio/tf/plprops.x177
-rw-r--r--sys/plio/tf/plr2li.x130
-rw-r--r--sys/plio/tf/plr2ll.x130
-rw-r--r--sys/plio/tf/plr2ls.x130
-rw-r--r--sys/plio/tf/plr2pi.x74
-rw-r--r--sys/plio/tf/plr2pl.x74
-rw-r--r--sys/plio/tf/plr2ps.x74
-rw-r--r--sys/plio/tf/plreqi.x27
-rw-r--r--sys/plio/tf/plreql.x27
-rw-r--r--sys/plio/tf/plreqs.x27
-rw-r--r--sys/plio/tf/plrpri.x56
-rw-r--r--sys/plio/tf/plrprl.x56
-rw-r--r--sys/plio/tf/plrprs.x56
-rw-r--r--sys/plio/tf/plrropi.x195
-rw-r--r--sys/plio/tf/plrropl.x195
-rw-r--r--sys/plio/tf/plrrops.x195
-rw-r--r--sys/plio/zzdebug.x1442
-rw-r--r--sys/plio/zzlib.x64
-rw-r--r--sys/plio/zzsum.x50
121 files changed, 12419 insertions, 0 deletions
diff --git a/sys/plio/PLIO.hlp b/sys/plio/PLIO.hlp
new file mode 100644
index 00000000..16e23dfc
--- /dev/null
+++ b/sys/plio/PLIO.hlp
@@ -0,0 +1,1341 @@
+.help PLIO Feb88 "Pixel List Package Design"
+.ce
+\fBThe IRAF Pixel List Package\fR
+.ce
+and
+.ce
+\fBIMIO Extensions to Support Image Masks\fR
+.ce
+Doug Tody
+.ce
+February, 1988
+.ce
+(Revised June 1988)
+
+
+.NH
+Introduction
+
+ The pixel list package is a general package for flagging individual pixels
+or regions of an image, to mark some subset of the pixels in an image.
+This may be done to flag bad pixels, or to identify those regions of an image
+to be processed by some applications program. When the pixel list package is
+used to flag the bad pixels in an image we call this a \fBbad pixel mask\fR,
+or BPM. When used to identify the regions of an image to be processed (or
+ignored), we call the list a \fBregion mask\fR.
+
+A pixel list may be viewed conceptually as either a list or an image; the list
+is merely the compressed form of a virtual \fBmask image\fR. Storing a mask
+image as a list has two major advantages.
+.ls
+.ls [1]
+Storage efficiency. Simple masks may be stored very compactly, e.g., as small
+arrays stored directly in an image header, or in a separate mask file or
+database.
+.le
+.ls [2]
+Runtime efficiency. Storing a mask in list form has the advantage that one
+can determine very quickly whether or not a given region of the image (e.g.,
+an image line) contains any pixels in the mask. The alternative, given a
+fully populated mask image (or flagging the pixels directly in the data image),
+requires that one examine every pixel in the mask image, which is very
+inefficient for simple masks.
+.le
+.le
+
+The pixel list technique for implementing image masks is the most efficient
+choice only for simple or moderately complex masks. As a mask image increases
+in complexity there eventually comes a point where it would be simpler and
+more efficient to use a conventional raster image to store the mask.
+For example, if one wishes to associate a flag value (such as a weight) with
+every pixel in an image, and the flag values vary rapidly in value across the
+image, a separate image should probably be used (except that other forms of
+data compression may be possible; the IRAF \fInoise function package\fR
+addresses one aspect of this problem).
+
+The pixel list related software is subdivided into two parts, the pixel list
+package itself, and the extensions to IMIO to make use of the pixel list
+package. The pixel list package in itself is independent of IMIO and may be
+used separately.
+
+.NH
+IMIO Support for Pixel Lists
+
+ Most image applications tend to fall into two classes, the simple pointwise
+transformation operators, and the more cpu intensive image analysis operations.
+The simple operators usually operate upon the whole image (no mask required)
+and may ignore the presence of bad pixels in the image, provided reasonable
+artificial values have been provided for the bad pixels so that arithmetic
+problems do not occur, and provided we keep track of the locations of the
+bad pixels (by propagating the bad pixel lists), so that the bad pixel
+information is available to subsequent image analysis programs.
+
+The image analysis operators, on the other hand, must know the locations of
+the bad pixels in order to exclude them from the fit. The analysis is also
+often performed only upon specified regions of the image, as indicated by some
+sort of image mask. Most image analysis algorithms are fairly cpu intensive,
+hence as long as we can access the bad pixel list and region mask reasonably
+efficiently, we would prefer a simple interface, e.g., for every buffer of
+input image data read, an associated buffer the same size and dimensionality
+as the input image data buffer, containing the bad pixel or region mask flag
+values.
+
+In general, if pixel lists are to be used with images, either IMIO must
+provide direct support for pixel lists or the pixel list package must duplicate
+much of the functionality of IMIO. This is because the pixel list must
+be defined in terms of the physical image, whereas an applications
+program accessing an image via IMIO may be operating upon some user specified
+section of the image. To support applications transparent sectioning, boundary
+extension, multiple input buffers, and so on, support for pixel lists must
+be integrated into IMIO.
+
+.NH 2
+Mask Images
+
+ These considerations lead us to make it possible for a pixel list to appear
+to an application as a special type of image called a \fImask image\fR, even
+though the mask may be stored as a pixel list internally, and accessed or
+queried directly as a list if desired. If whenever we read a block of data
+from the data image, we read an equivalent block of data from the mask image,
+it is evident that whatever we come up with for reading from the mask image is
+going to have to look an awful lot like IMIO. Given the complexities of image
+section transformations, automatic buffer allocation strategies, and so on,
+the simplest solution is to just go ahead and use IMIO to access the virtual
+mask image. This leads us to the interface shown in the figure below.
+These routines represent an extension to IMIO to support pixel lists. The
+conventional IMIO routines, used to access the mask "pixels", are not shown.
+
+The mask image may be opened by name with \fIim_pmmap\fR, or an already opened
+pixel mask, perhaps the result of some computation done by the applications
+program, may be opened with \fIim_pmmapo\fR. Alternatively, if the mask is
+stored (or will be stored in the case of a new mask) in a pixel list file
+(".pl" extension), the mask may be opened with a conventional \fIimmap\fR call.
+This last option is especially powerful, since it allows all image tasks to
+access masks as if they were conventional images, e.g., one can DISPLAY or
+IMCOPY a stored mask.
+
+.ks
+.nf
+ im = im_pmmap (maskname, mode, ref_im|NULL)
+ im = im_pmmapo (pm, ref_im)
+
+ imseti (im, IM_RLIO, YES|NO)
+ imseti (im, IM_PMDES, pm)
+ pm = imstati (im, IM_PMDES)
+
+ bool = im_pmlne[123] (im[, lineno[, bandno]])
+ bool = im_pmsne[123] (im, x1, x2[, y1, y2[, z1, z2]])
+ bool = im_pmlnev (im, v)
+ bool = im_pmsnev (im, vs, ve, ndim)
+.fi
+.ke
+
+The \fImode\fR argument to \fIim_pmmap\fR specifies both the access mode for
+the mask, and any standard transformations to be performed upon the mask before
+i/o takes place (if an existing mask is to be read). NEW_FILE mode is used to
+create a new mask; READ_ONLY to read an existing mask, and READ_WRITE to modify
+an existing mask. The mode-transformation flags currently supported are
+INVERT_MASK, which performs a PIX_NOT operation on the input mask, and
+BOOLEAN_MASK, which converts an integer input mask to boolean. Any more
+complex mask transformations or combinations must be performed explicitly
+by the calling program via calls to the PMIO or PLIO routines, mapping the
+resultant mask onto an image descriptor with \fIim_pmmapo\fR.
+
+If a reference image \fIref_im\fR is specified at open time,
+then the mask image will inherit any image section, boundary extension, etc.
+in effect for the reference image. Inheritance occurs when the first i/o
+to the mask image takes place. The reserved names "BPM" and "EMPTY",
+respectively (must be upper case), denote the bad pixel mask
+for the reference image, or an empty mask the size of the reference image.
+If the reference image does not have a bad pixel mask, BPM and EMPTY are
+equivalent. Note that separate image descriptors are used for the data
+(reference) image and any mask images. Multiple mask images may be associated
+with the same reference image.
+
+Normal IMIO calls, e.g., \fIimgs2i\fR, are used to access the mask "pixels".
+Since it is common for a mask to be empty over large regions of an image,
+a set of boolean functions are provided for testing whether regions of the
+mask are nonempty, allowing a program to avoid the expense of image mask i/o
+and subseqent testing of the mask pixel values if not needed.
+The boolean functions \fIim_pmlne\fR (line not empty) and \fIim_pmsne\fR
+(section not empty) and their more general variants \fIim_pmlnev\fR and
+\fIim_pmsnev\fR test whether the specified region of the mask image contains
+any nonzero mask pixels. The calling sequences of these routines are patterned
+after the corresponding IMIO pixel i/o routines, e.g., \fIim_pmlne2\fR is
+intended for use with the \fIimgl2\fR routines, and \fIim_pmlnev\fR is
+intended for use with the \fIimgnl\fR routines.
+
+By default, mask image i/o operates on data buffers containing arrays of
+pixels, as for a conventional data image. When i/o takes place, the interface
+automatically converts between pixel format and the compressed line list format
+in which the mask is stored internally. An alternative format for the mask
+data is \fBrange list\fR format, enabled by setting the \fIIM_RLIO\fR parameter
+to YES in an \fIimseti\fR call. Range list i/o works identically to pixel i/o,
+except that the "pixel arrays" returned by IMIO (or input to IMIO) are range
+list structures, as defined in \fB<plset.h>\fR and discussed in section 3.3.4.
+
+Range list i/o at the IMIO level is fully general, i.e., the section
+transformation, if any, is applied transparently to the calling program,
+and the full range of IMIO i/o routines may be used.
+If the data buffer is a multidimensional subraster, each line of the subraster
+will be a separate range list, and the physical length in pixels of each line
+of the subraster will be as for a pixel subraster.
+On a read, if the length of the encoded range list exceeds the subraster
+line length in pixels, and i/o error will occur. Such an i/o error cannot
+occur when i/o is restricted to lines or line segments (1D buffers),
+since IMIO will automatically allocate a data buffer large enough to hold
+the worst case (longest) range list. Range list overflow errors are
+unlikely even for subrasters, however, since the range list format is
+normally much more compact than the equivalent pixel array (except for
+very small subrasters or very complex masks).
+
+.NH 2
+Masked Image I/O (MIO)
+
+ The mask image i/o routines discussed in the previous section provide a
+fully generalized means for directly accessing a mask as an separate entity,
+independent of the associated reference data image. Two separate image
+descriptors are required, and two parallel but separate sets of calls to the
+IMIO routines. In many applications, however, a mask is used only to specify
+those regions of the data image to be analyzed or processed. We want to
+access those regions of the image visible through the mask, but are not
+otherwise interested in the mask itself. The \fBmasked image i/o\fR (MIO)
+interface is designed for such applications.
+
+The MIO interface is summarized in the figure below. A named mask may be
+opened on a previously opened image with the \fImio_open\fR procedure.
+As for \fIim_pmopen\fR, the reserved names "BPM" and "EMPTY", respectively,
+denote the bad pixel mask for the image, or an empty mask the size of the
+image. If the image does not have a bad pixel mask, an empty mask will be
+opened.
+
+The \fIflags\fR argument, if nonzero, specifies an operation be performed
+upon the input mask to generate the mask to be used to access the data image.
+The currently supported flags are INVERT_MASK and BOOLEAN_MASK. For example,
+when the mask is a bad pixel mask, INVERT_MASK would cause MIO to access only
+that portion of the image which is \fInot\fR covered by the bad pixel mask,
+i.e., the "good" region of the image. BOOLEAN_MASK is used to convert an
+integer mask into a boolean mask. Note that in the case of inversion of an
+integer mask, the PIX_NOT operation merely complements the mask pixel values,
+hence will not affect the \fIregion\fR covered by the mask. To invert an
+integer mask in the region sense, use INVERT_MASK+BOOLEAN_MASK.
+
+.ks
+.nf
+ mp = mio_open (maskname, flags, im)
+ mp = mio_openo (pm, im)
+ value = mio_stati (mp, param)
+ mio_seti (mp, param, value)
+ mio_setrange (mp, vs, ve, ndim)
+ n|EOF = mio_[gp]lseg[silrdx] (mp, ptr, mval, v, npix)
+ mio_close (mp)
+.fi
+.ke
+
+More general mask transformations must be carried out by the user using the
+PMIO package to directly compute the desired mask, the mapping the mask onto
+an image descriptor with \fImio_openo\fR. For example, given as input a bad
+pixel mask and a region mask, one could compute the mask specifying all pixels
+in the region mask but not in the bad pixel mask. Any general mask may be
+computed in this way.
+
+An MIO application will not normally need to access the mask directly, but
+if such access is required the mask descriptor may be obtained with a call
+to \fImio_stati\fR to fetch the value of the parameter \fIP_PMDES\fR.
+Similarly, the image descriptor may be queried as parameter \fIP_IMDES\fR,
+and either parameter may be set with the corresponding call to \fImio_seti\fR.
+
+Successive calls to a get or put line segment procedure, e.g.,
+\fImio_glsegr\fR (get line segment real), return line segments from the data
+image until all the data present in the area outlined by the mask has been
+accessed, at which time EOF is returned as the function value.
+Each line segment is returned along with a vector \fIv\fR specifying the
+line of the image from which the segment is taken and the index of the first
+pixel of the current line segment within the line, a count \fInpix\fR of the
+number of pixels in the line segment, and the mask value \fImval\fR for the
+current line segment (e.g., 1 for a boolean mask). Each line segment
+corresponds to a region of constant nonzero value (\fImval\fR) in the mask.
+Line segments are returned sequentially in storage order.
+
+By default, the entire region of the image visible through the mask will be
+accessed. To access only a portion of the image, the \fIimio_setrange\fR
+procedure may be called before performing any i/o to specify the starting and
+ending vector coordinates \fIvs\fR and \fIve\fR of the region to be accessed.
+Multiple calls may be made on the same MIO descriptor to access multiple
+regions of the data image, or to "rewind" a given region of the image.
+
+.NH 2
+Mask Image Storage
+
+ As we shall see in the discussion of the pixel list package, the pixel
+list package is designed to permit a list to be stored anywhere, e.g.,
+in a binary file, in a database, as an array in an image header,
+or merely as a temporary array in memory. When the new image structures
+become available storing the masks in the image header or in a global database
+will probably be the best approach, but in the meantime the simplest solution
+is to store each pixel list or mask in a small binary file. This approach has
+the advantage of being independent of the particular image format used (none
+of which are currently capable of storing the pixel list directly in any case).
+
+If desired, the name of the mask file may be stored in the image header
+as a simple string valued parameter. Alternatively, the mask name may be
+input as a parameter when a task is run. A single mask may be associated
+with several images, or several masks may be used with the same image.
+The approach to be followed for a particular program is up to the programmer
+or package designer.
+
+The bad pixel mask is a special case, since it has a well defined logical
+meaning for an image, unlike the region masks which are application specific.
+The most straightforward approach is to use a single boolean mask for the
+bad pixel list, using the optional header keyword \fIBPM\fR to store the
+mask name, which should normally be the image name plus the pixel list file
+extension "\fI.pl\fR". An integer BPM could also be used, but most applications
+would treat it as a boolean mask, treating any pixel with a nonzero value as
+a bad pixel.
+
+Any additional information required to describe the bad pixels is application
+specific, and may be stored in any of several ways, e.g., as a set of boolean
+masks, in an integer mask, using flag bits or reserved values to describe each
+pixel or region, in a separate fully populated weight image, or using the noise
+function package. Most IRAF applications will probably use the BPM plus a
+noise function, with the noise function being stored either as
+a one-dimensional noise model or as a separate uncertainty image, transparently
+to the application. The combination of a one-dimensional noise model plus a
+compressed bad pixel list should provide an efficient and flexible solution
+to the pixel variance problem for most applications.
+
+.NH 2
+Example
+
+ Open a data image and the associated mask image, and sum the pixels within
+the area indicated by the mask.
+
+.nf
+ include <pmset.h>
+
+ task sum = t_sum
+.fi
+
+.tp 6
+.nf
+ # SUM -- Sum the image pixels lying within the given mask.
+
+ procedure t_sum()
+
+ char image[SZ_FNAME] # input data image
+ char mask[SZ_FNAME] # image mask
+
+ int npix, mval, totpix, m_flags
+ long v[PM_MAXDIM]
+ pointer im, mp, pp
+ real sum
+
+ bool clgetb()
+ real asumr()
+ int mio_glsegr()
+ pointer immap(), mio_open()
+
+ begin
+ call clgstr ("image", image, SZ_FNAME)
+ call clgstr ("mask", mask, SZ_FNAME)
+ m_flags = 0
+ if (clgetb ("invert"))
+ m_flags = INVERT_MASK
+
+ im = immap (image, READ_ONLY, 0)
+ mp = mio_open (mask, m_flags, im)
+
+ sum = 0; totpix = 0
+ while (mio_glsegr (mp, pp, mval, v, npix) != EOF) {
+ sum = sum + asumr (Memr[pp], npix)
+ totpix = totpix + npix
+ }
+
+ call mio_close (mp)
+ call imunmap (im)
+
+ call printf ("%d pixels, sum=%g, mean=%g\n")
+ call pargi (totpix)
+ call pargr (sum)
+ if (totpix > 0)
+ call pargr (sum / totpix)
+ else
+ call pargr (INDEF)
+ end
+.fi
+
+A more complex application might use the spatial information provided by
+\fIv\fR and \fInpix\fR, or the flag values provided by \fImval\fR (for an
+integer mask). For example, a surface fitting routine would accumulate each
+line segment into a least squares matrix, using the coordinate information
+provided as well as the pixel values.
+
+.NH 2
+The Pixel Mask Package (PMIO)
+
+ We have thus far discussed two quite different interfaces, i.e., the use
+of IMIO to do pixel i/o to a mask mapped as a virtual mask image, and the MIO
+interface, used to access the portion of a data image visible through a mask.
+The next step is to define the interface used to access the mask object
+directly as a \fImask\fR, independently of the use of masks for image i/o.
+
+.nf
+ pm = pm_newmask (ref_im, depth)
+
+ pm = pm_open (bufptr|NULL)
+ pm = pm_create (naxes, axlen, depth)
+ pm = pm_newcopy (pm)
+ pm_close (pm)
+
+ pm_[sg]size (pm, naxes, alxen, depth)
+ pm_seti (pm, param, value)
+ value = pm_stati (pm, param)
+ pm_debug (pm, outfd, maxcol, flags)
+ bool = pm_empty (pm)
+ pm_compress (pm)
+ pm_clear (pm)
+
+ pm_load (pm, bufptr)
+ nwords = pm_save (pm, bufptr, buflen)
+ pm_loadf (pm, fname, title, maxch)
+ pm_savef (pm, fname, title, save_flags)
+ pm_[load|save]im (pm, imname[, save_flags])
+
+ ptr = pm_access (pm, v)
+ bool = pm_linenotempty (pm, v)
+ bool = pm_sectnotempty (pm, vs, ve, ndim)
+ pm[gp]l[lrp][sil] (pm, v, buf, b_depth, npix, rop)
+
+ pm_[set|get]plane (pm, v)
+ pm_point (pm, x, y, rop)
+ pm_circle (pm, x, y, r, rop)
+ pm_box (pm, x1,y1, x2,y2, rop)
+ pm_line (pm, x1,y1, x2,y2, width, rop)
+ pm_polygon (pm, x, y, npts, rop)
+
+ pm_rop (pm_src, vs, pm_dst, vs, vn, rop)
+ pm_stencil (pm_src, vs, pm_dst, vs, pm_stl, vs, vn, rop)
+.fi
+
+There are two variants on this lowest level interface, known as PMIO (pixel
+mask i/o) and PLIO (pixel list i/o). These two interfaces are equivalent
+with one difference: PMIO can accept a reference image and inherit the section
+transformation of the reference image, allowing the mask to be accessed in
+the coordinate system of the reference image, while PLIO is a stand alone
+interface, implemented independently of IMIO without any ties to IMIO.
+PMIO is implemented as a thin layer upon PLIO, with ties to IMIO to access
+the section transformation for the reference image.
+
+The PMIO interface is shown in the figure above.
+With the exception of the additional routine \fIpm_newmask\fR, used to create
+a new, empty mask the same size as a reference image, the PMIO routines are
+identical to the corresponding PLIO routines except for the \fIpm\fR package
+prefix, and the implied section transformation. Indeed, if no reference image
+is specified or if the section transformation is unitary (the reference image
+was opened without an image section), the two interfaces are identical.
+Hence the discussion of PLIO in the next section will serve to document PMIO
+as well.
+
+To use PMIO, merely include \fB<pmset.h>\fR rather than \fI<plset.h>\fR,
+and set the reference image with a call to \fIpm_seti\fR, e.g.,
+
+ call pm_seti (pm, P_REFIM, ref_im)
+
+This step can be skipped if \fIpm_newmask\fR or \fIpm_newcopy\fR is used to
+create a new mask, as the reference image will be inherited by the new mask.
+
+Programs which use PMIO to access a mask may also use the low level line,
+range, and pixel list routines discussed in section 3.1 (\fBpl_rangerop\fR etc.)
+on lists returned by PMIO, since PMIO will have already transformed the data
+into the coordinate system of the reference image.
+
+In general, the PMIO interface should be used in preference to PLIO whenever
+the mask to be accessed is logically associated with some data image or images.
+The region description and logical region processing capabilities of PLIO are
+very powerful in their own right, however, and PLIO should be used directly by
+applications which do not consider the mask to be merely an overlay for a data
+image. An example would be any application which operates upon a 2-dimensional
+data structure other than an image (e.g., region filtering in the POE image
+kernel).
+
+.NH
+The Pixel List Package (PLIO)
+
+ A pixel list is a way of representing an N-dimensional image matrix which
+is well suited for applications where the represented image is sparse, or
+consists of a moderate number of arbitrarily shaped regions. Routines are
+provided for creating new lists, for writing to or reading from lists,
+for storing or retrieving lists, and for performing various types of
+operations upon entire lists to make new lists. The full set of routines
+in the package are summarized in the figure below.
+
+A pixel list, like an image, has a fixed dimensionality and size which is
+determined at list creation time for the lifetime of the list. New lists
+are created with \fIpl_create\fR, which returns a pointer to an empty list.
+The \fIdepth\fR parameter specifies the depth of the list in bits, i.e.,
+the number of bits per pixel, in the range 1-27. A boolean list has a depth
+of 1 bit. In the current implementation the boolean mask is handled as a
+degenerate case of an integer mask, i.e., an integer mask which happens to
+have mask values in the range 0-1.
+
+An existing list is accessed by opening a descriptor with \fIpl_open\fR and
+loading the list into the runtime descriptor structure, either by specifying
+a non-NULL buffer pointer \fIbufptr\fR, or by calling one of the \fIpl_load\fR
+functions after opening a null descriptor.
+
+.ks
+.nf
+ pl = pl_open (bufptr|NULL)
+ pl = pl_create (naxes, axlen, depth)
+ pl = pl_newcopy (pl)
+ pl_close (pl)
+
+ pl_[sg]size (pl, naxes, axlen, depth)
+ pl_seti (pl, param, value)
+ value = pl_stati (pl, param)
+ pl_debug (pl, outfd, maxcol, flags)
+ bool = pl_empty (pl)
+ pl_compress (pl)
+ pl_clear (pl)
+
+ pl_load (pl, bufptr)
+ nwords = pl_save (pl, bufptr, buflen)
+ pl_loadf (pl, fname, title, maxch)
+ pl_savef (pl, fname, title, save_flags)
+ pl_[load|save]im (pl, imname[, save_flags])
+
+ ptr = pl_access (pl, v)
+ bool = pl_linenotempty (pl, v)
+ bool = pl_sectnotempty (pl, vs, ve, ndim)
+ pl[gp]l[lrp][sil] (pl, v, buf, b_depth, npix, rop)
+
+ pl_[set|get]plane (pl, v)
+ pl_point (pl, x, y, rop)
+ pl_circle (pl, x, y, r, rop)
+ pl_box (pl, x1,y1, x2,y2, rop)
+ pl_line (pl, x1,y1, x2,y2, width, rop)
+ pl_polygon (pl, x, y, npts, rop)
+
+ pl_rop (pl_src, vs, pl_dst, vs, vn, rop)
+ pl_stencil (pl_src, vs, pl_dst, vs, pl_stl, vs, vn, rop)
+.fi
+.ke
+
+Pixel lists are stored externally as opaque binary byte arrays.
+The function \fIpl_save\fR will encode a pixel list as a byte array and store
+it in the indicated buffer, resizing the buffer if necessary.
+If \fIbufptr\fR is NULL a new buffer will be allocated, overwriting the NULL.
+The \fIpl_load\fR function performs the inverse function.
+The \fIf\fR suffixed functions are provided for convenience when storing
+lists in small binary files. The \fIim\fR suffixed functions create masks
+out of actual data images, and vice versa. In the most general case, a list
+is encoded into an array in memory and then stored away by the applications
+wherever they wish, e.g., as an array parameter in an image header when the
+new image structures become available.
+
+Pixel list i/o is provided via the \fIpl[gp]l[lrp][sil]\fR family of functions,
+which read, write, or edit (via the \fIrop\fR) lines or line segments of masks.
+The naming convention is as follows:
+
+.ks
+.nf
+ pl package prefix
+ [gp] get or put
+ l line segment
+ [lrp] as a line list, range list, or pixel array
+ [sil] in an array of type short, int, or long
+.fi
+.ke
+
+For example, \fIplglps\fR would get a line from a mask image as a fully
+populated array of type short. For maximum efficiency, since this is a low
+level interface, the data is read from or copied into a user allocated buffer,
+rather than having the pixel list package control the buffer.
+
+The functions \fIplgll[sil]\fR return the packed line list for the indicated
+line or line segment. This is the copy-out form of access with the least
+overhead, but requires that the application have knowledge of the internal
+line list format.
+
+Alternatively, if it is only desired to read from a list, accessing the list
+in the internal format, the internal list for an image line may be directly
+accessed by pointer with the \fIpl_access\fR function. This is the most
+efficient form of access. The variant \fIpl_linenotempty\fR is similar except
+that the NULL pointer is returned if the indicated line of the list is empty,
+providing an efficient and convenient way to perform the empty test on mask
+image lines.
+
+The functions \fIplglr[sil]\fR are similar to the get line list form of access,
+but instead of returning the encoded list-list in the internal format,
+it returns a simple array of ranges of absolute pixel indices and associated
+mask values. This is the recommended way of accessing the mask as a list
+structured object, since it does not require knowledge of the internal packed
+line list format. For example, to print line \fIv\fR of the mask on descriptor
+\fIpl\fR as a series of range lists:
+
+.ks
+.nf
+ int buf[3,1024], n, i, plglri()
+
+ n = plglri (pl, v, buf, 0, axlen[1], 0)
+ do i = 1, n {
+ call printf ("range at %d, %d pixels, mask value = %o\n")
+ call pargi (buf[1,i]); call pargi (buf[2,i])
+ call pargi (buf[3,i])
+ }
+.fi
+.ke
+
+These routines require that the depth in bits of the output line or range
+list or pixel array be specified. This is necessary to avoid generating
+numbers the size of the machine integer when inverting a mask (PIX_NOT),
+e.g., if the mask depth is 8 bits, the complement of a 0 pixel should be 377,
+not 37777777777. The depth argument may also be used to convert an integer
+mask to a boolean mask, or vice versa (using the PIX_VALUE field of the
+rasterop discussed in the next section). If \fIb_value\fR is zero, clipping
+of the output values is disabled. This would be appropriate, for example,
+when simply reading from a mask without modifying the pixel values.
+
+New pixel lists may be created by having the application prepare the packed
+line lists externally, inserting them directly into the pixel list structure
+with a \fIput\fR routine. This is generally inadvisable, however, because
+the packed line list format is considered internal to the PLIO package.
+A better alternative is to input the mask data as a populated array or
+as a range list, letting the PL package manage the internal line list.
+
+A more convenient approach to creating or modifying masks for most applications
+is to define the mask by specifying a series of include and exclude standard
+region types (circles, boxes, lines, points, or polygons),
+using the block of routines beginning with \fIpl_setplane\fR in the figure.
+Note that these are two dimensional
+operators; they are provided for convenience even though the pixel list
+package can support images of any dimension (if the image has three or more
+dimensions \fIpl_setplane\fR may be used to specify the plane to be operated
+upon). The most general routine is \fIpl_polygon\fR, which may be used to
+operate upon the pixels in the interior of any general polygon. All of the
+region drawing operators will permit regions to extend beyond the boundary
+of the mask, clipping as necessary at the boundary.
+
+.NH 2
+Pixel, Line, and Range List Routines
+
+ Most of the N dimensional region or mask oriented PMIO and PLIO routines
+eventually result in calls to the low level pixel, line, and range rasterop
+routines and format conversion routines shown in the figure below. These
+routines form the functional core of the PLIO package and will often be
+responsible for most of the execution time of routines which use PLIO.
+
+There are two main classes of routines. The \fIpl_pixrop\fR, \fIpl_linerop\fR,
+and \fIpl_rangerop\fR routines perform the general raster operation on pixel
+arrays, line lists, and range lists. The \fIpl_linestencil\fR routine performs
+the stencil rasterop operation upon line lists; currently only a list list
+version is available since this is a rarely used routine. Lastly, a set of
+six routines are provided for converting between any two of the three formats,
+e.g., line list to range list or pixel array and vice versa, omitting the
+like-to-like conversions. For example, \fIpl_p2ri\fR would convert a pixel
+array to a range list, both of type integer.
+
+.ks
+.nf
+ pl_linerop (ll_src, xs, src_maxval,
+ ll_dst, ds, dst_maxval, ll_out, npix, rop)
+ pl_linestencil (ll_src, xs, src_maxval, ll_dst, ds, dst_maxval,
+ ll_stn, xs, ll_out, npix, rop)
+
+ pl_pixrop[sil] (px_src, xs, src_maxval,
+ px_dst, ds, dst_maxval, npix, rop)
+ pl_rangerop[sil] (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+
+ n = pl_[lrp]2[lrp][sil] (op_src, xs, op_dst, npix)
+.fi
+.ke
+
+Note that these low level routines
+specify the mask depth as a maximum pixel value, rather than taking the depth
+in bits as the high level PMIO and PLIO routines do, and that there are no
+\fI"pm_"\fR versions of these routines - the one set of routines may be used
+with both PLIO and PMIO.
+
+.NH 2
+Rasterops
+
+ The argument \fIrop\fR in the circle, box, and other routines discussed
+in the previous sections is called a \fBrasterop\fR, and specifies the bitwise
+operation to be performed to generate the destination operand. Much of the
+generality and conciseness of the pixel list package is due to the rasterop
+abstraction, which is patterned after a similar construct used by the Sun
+Microsystems \fISunview\fR interface to specify operations upon \fIpixrects\fR,
+which are logically similar to PLIO masks.
+
+The rasterop defines the operation to be performed to generate the destination
+mask, in terms of bitwise boolean operations performed upon the input source
+and destination masks. Rasterops are constructed via a series of bitwise
+\fIand\fR and \fIor\fR operations, using the following macro defines:
+
+.ks
+.nf
+ PIX_SRC specifies the source mask
+ PIX_DST specifies the destination mask
+ PIX_NOT(op) inverts the operand mask
+ PIX_VALUE(value) specifies the bitplanes to be set
+.fi
+.ke
+
+Examples of some of the possible operations (out of a total of 16 possible
+operations) are shown below. This table is reproduced from the SunView
+Pixrect Reference Manual.
+
+.ks
+.nf
+ \fIRasterop\fR \fIDescription\fR
+
+ PIX_SRC copy source to destination
+ PIX_DST no-op
+ PIX_SRC | PIX_DST paint (OR source to destination)
+ PIX_SRC & PIX_DST mask (AND of source and destination)
+ PIX_NOT(PIX_SRC)&PIX_DST erase (AND destination with negation
+ of source)
+ PIX_NOT(PIX_DST) invert area (negate the existing
+ values)
+ PIX_SRC ^ PIX_DST inverting paint (XOR of source and
+ destination)
+.fi
+.ke
+
+Here, the |&^ denote the SPP \fIor\fR, \fIand\fR, and \fIxor\fR intrinsic
+functions, which must be used to construct actual rasterop expressions in SPP,
+e.g.:
+
+ PIX_NOT(PIX_SRC) | PIX_DST | PIX_VALUE(v)
+
+would actually be written as
+
+ or (PIX_NOT(PIX_SRC), PIX_DST) + PIX_VALUE(v)
+
+The following additional macros are defined to deal with the more common
+cases of setting or clearing a region.
+
+.ks
+.nf
+ PIX_SET (PIX_SRC | PIX_NOT(PIX_SRC))
+ PIX_CLR (PIX_SRC & PIX_NOT(PIX_SRC))
+.fi
+.ke
+
+As a simple example, consider the case of specifying a region mask as a series
+of include and exclude circles and boxes. This is trivial for a boolean mask:
+an include circle or box is specified by the rasterop PIX_SET, and an exclude
+by PIX_CLR. In the equivalent operation upon an integer mask this would cause
+any mask values which had already been set to be replaced, hence it might be
+desirable to use a PIX_SRC|PIX_DST rasterop instead, to OR the bits of the new
+flag values into those of any flag values already set. Similarly, when using
+\fIpl_ltop\fR to unpack a line list into a pixel array, the PIX_SRC|PIX_DST
+rasterop might be specified to OR the mask segment into an existing pixel
+array.
+
+General bitwise boolean operations upon masks are provided by the \fIpl_rop\fR
+and \fIpl_stencil\fR operators, which operate upon entire masks or rectangular
+subregions of masks. The \fIpl_rop\fR operator combines the source and
+destination masks to produce a new mask; \fIpl_stencil\fR performs the same
+operation, but only in the regions specified by the stencil mask,
+which must be a boolean mask. The input mask may be NULL (depending upon
+the rasterop specified), or the source and destination masks may be the same.
+
+The equivalent operators for line lists, range lists, and pixel arrays are
+the \fIpl_S2D\fR family of routines, where \fIS=D=[lrp]\fR for a line list,
+range list, or pixel array rop. The routine \fIpl_linestencil\fR implements
+the stencil operation for a line list.
+
+As noted above, when operating upon integer masks (\fIdepth\fR > 1), the
+PIX_VALUE macro is used to specify the flag value for the indicated region.
+For example, the following rasterop would set all the pixels in a region to
+the same value:
+
+ PIX_VALUE(value)
+
+to OR in the new value instead of replacing any existing flag values:
+
+ PIX_VALUE(value) | PIX_DST
+
+If a boolean mask is to be written to an integer mask, PIX_VALUE specifies
+the flag value to be used for the regions set to 1 in the input boolean mask.
+For example, one might wish to combine a set of 8 boolean masks to form a
+single 8 bit deep integer mask, with each bit in the integer mask corresponding
+to one of the input boolean masks (001 = mask 1, 002 = mask 2, 040 = mask 3,
+etc.). This could be done using the rasterop shown above, incrementing
+PIX_VALUE in each operation to specify the bitplane to be set.
+
+.NH 3
+Rasterop Expression Evaluation
+
+ As mentioned above, there are sixteen possible ways to combine the source
+and destination masks subject to the four possible boolean operations
+(\fIand\fR, \fIor\fR, \fIxor\fR, and \fInot\fR).
+More precisely, although numerous bitwise expressions can be constructed, many
+of these are equivalent, and there are only sixteen fundamental operations.
+These are summarized in the following table.
+
+.nf
+ Operation Opcode
+
+ PIX_CLR 00
+ PIX_SET 17
+
+ PIX_SRC 14
+ PIX_DST 12
+
+ PIX_NOT(PIX_SRC) 03
+ PIX_NOT(PIX_DST) 05
+
+ PIX_SRC & PIX_DST 10
+ PIX_SRC | PIX_DST 16
+ PIX_SRC ^ PIX_DST 06
+
+ PIX_SRC & PIX_NOT(PIX_DST) 04
+ PIX_SRC | PIX_NOT(PIX_DST) 15
+ PIX_NOT(PIX_SRC) & PIX_DST 02
+ PIX_NOT(PIX_SRC) | PIX_DST 13
+
+ PIX_NOT (PIX_SRC & PIX_DST) 07
+ PIX_NOT (PIX_SRC | PIX_DST) 01
+ PIX_NOT (PIX_SRC ^ PIX_DST) 11
+.fi
+
+As an example of the redundancy of arbitrary rasterop expressions,
+compare the following with the equivalent expressions (same opcode)
+in the table above.
+
+.ks
+.nf
+ PIX_NOT(PIX_SRC) & PIX_NOT(PIX_DST) 01
+ PIX_NOT(PIX_SRC) ^ PIX_NOT(PIX_DST) 06
+ PIX_NOT(PIX_SRC) | PIX_NOT(PIX_DST) 07
+ PIX_NOT(PIX_SRC) ^ PIX_DST 11
+ PIX_SRC ^ PIX_NOT(PIX_DST) 11
+.fi
+.ke
+
+Any number of additional logically redundant expressions may be constructed.
+The programmer should not worry about simplifying logical expressions,
+but should choose instead whatever expression is clearest for their particular
+application, letting the interface handle expression optimization internally.
+
+Of the sixteen possible fundamental operations, there are four trivial
+operations (\fIclr\fR, \fIset\fR, \fIsrc\fR, \fIdst\fR),
+seven composite operations, and four primary operations, namely,
+\fIand\fR, \fIor\fR, \fIxor\fR, and \fInot\fR (two cases of the latter).
+The composite operations are all implementable as two primary
+operations in sequence. PIX_NOT is implemented by actual inversion of the
+list rather than as a mode flag, to avoid complications when the list is read.
+
+.NH 3
+Rasterop Encoding
+
+ The rasterop argument specifies the bitwise boolean operation to be
+performed, and optionally the pixel value to be used (in the case of an
+integer mask). Both are specified as a single integer value, packed as
+shown in the figure below.
+
+.ks
+.nf
+ +--+-------------+--------+
+ |32|31 5|4 1|
+ +--+-------------+--------+
+ | | pixel value | opcode |
+ +--+----------------------+
+.fi
+.ke
+
+The following is an example of a typical rasterop (note that since the pixel
+value field has a reserved range of bits which is zero prior to expression
+evaluation, the "+" operator is equivalent to the \fIor\fR intrinsic function).
+
+ or(PIX_SRC, PIX_NOT(PIX_DST)) + PIX_VALUE(pix_value)
+
+Note that the width of the pixel value field in the rasterop constrains the
+effective mask depth to be 27 bits or less, if full generality is desired in
+rasterop operations. Masks of up to 31 bits (the minimum unsigned precision
+of a \fIlong\fR) can be stored and accessed, but rasterops using PIX_VALUE
+are limited to 27 bits.
+
+.NH 2
+Pixel List Data Structures
+
+ A pixel list consists of an array of pointers to the \fIline lists\fR
+forming the mask. There is one pointer for each image line; if the pointer
+is NULL, no mask values are set on the associated image line. N-dimensional
+images are easily handled by an N-1 dimensional array of line pointers.
+
+Each line list consists of a series of offsets and mask pixel values
+(the pixel values are normally omitted for a boolean mask).
+The offsets specify the number of pixels for which the mask has the same value.
+This line list format has the following two significant advantages over the
+alternative technique of specifying a list of ranges using absolute pixel
+coordinates:
+.ls
+.ls [1]
+A higher degree of data compression is possible. If absolute pixel
+coordinates are used in the list, the list must be an array of 32 bit
+integer values in order to avoid a builtin limit on the size of the image
+which can be represented. By using offsets, list elements as small as
+one byte are possible.
+.le
+.ls [2]
+A list composed of offsets is invariant with respect to translation.
+For example, when extracting a subraster of an image, one can extract the
+corresponding segment of each line list and perhaps edit the first element,
+and the remainder of the list may be used without change.
+.le
+.le
+
+When describing regular regions such as boxes it will often be the case that
+successive lines of the mask are equivalent, in which case multiple line list
+pointers may point to the same line list. Hence, the line lists will provide
+good compression of regular objects in any two dimensional plane of the mask.
+This technique can be extended to higher dimensions if desirable, without
+affecting the line list data structures visible to an application.
+
+.NH 3
+Line List Encoding
+
+ A good compromise between storage efficiency and efficiency of runtime
+access, while keeping things simple, is achieved if we maintain the compressed
+line lists as variable length arrays of type short integer (16 bits per list
+element), regardless of the mask depth. A line list consists of a series of
+simple \fIinstructions\fR which are executed in sequence to reconstruct a line
+of the mask. Each 16 bit instruction consists of the sign bit (not used at
+present), a three bit \fIopcode\fR, and twelve bits of data, i.e.:
+
+.ks
+.nf
+ +--+-----------+-----------------------------+
+ |16|15 13|12 1|
+ +--+-----------+-----------------------------+
+ | | opcode | data |
+ +--+-----------------------------------------+
+.fi
+.ke
+
+The significance of the data depends upon the instruction. The instructions
+currently implemented are summarized in the table below.
+
+.ks
+.nf
+ Instruction Opcode Description
+
+ ZN 00 Output N zeros
+ HN 04 Output N high values
+ PN 05 Output N-1 zeros plus one high value
+ SH 01 Set high value, absolute
+ IH,DH 02,03 Increment or decrement high value
+ IS,DS 06,07 Like IH-DH, plus output one high value
+.fi
+.ke
+
+In order to reconstruct a mask line, the application executing these
+instructions is required to keep track of two values,
+the \fIcurrent high value\fR and the \fIcurrent position\fR in the output line.
+The detailed operation of each instruction is as follows:
+.ls 4
+.ls 8 ZN
+Zero the next N (=\fIdata\fR) output pixels.
+.le
+.ls HN
+Set the next N output pixels to the current high value.
+.le
+.ls PN
+Zero the next N-1 output pixels, and set pixel N to the current high value.
+.le
+.ls SH
+Set the high value (absolute rather than incremental), taking the high 15 bits
+from the next word in the instruction stream, and the low 12 bits from the
+current data value.
+.le
+.ls IH,DH
+Increment (IH) or decrement (DH) the current high value by the \fIdata\fR
+value. The current position is not affected.
+.le
+.ls IS,DS
+Increment (IS) or decrement (DS) the current high value by the \fIdata\fR
+value, and \fIstep\fR, i.e., output one high value.
+.le
+.le
+
+The high value is assumed to be set to 1 at the beginning of a line, hence
+the IH,DH and IS,DS instructions are not normally needed for boolean masks.
+If the length of a line segment of constant value or the difference between
+two successive high values exceeds 4096 (12 bits), then multiple instructions
+are required to describe the segment or intensity change.
+
+The performance of this encoding is very good for typical masks consisting
+of isolated high or low values or extended regions at the same level.
+The worst case performance occurs when successive pixels have different values.
+Even in this case the encoding will only require one word (16 bits) per mask
+pixel, provided either the delta intensity change between pixels is usually
+less than 12 bits, or the mask represents a zero floored step function of
+constant height. The worst case cannot exceed npix*2 words provided the mask
+depth is 24 bits or less.
+
+.NH 3
+Example: Line List Encoding
+
+ As a simple example, consider the following line of a boolean mask.
+The set pixels are 1, 4, 8 through 11, and so on, shown as \fIindex list\fR
+in the figure. The corresponding \fIoffset list\fR (line list encoding)
+is also shown. Note that both encodings require the same amount of space,
+assuming 16 bits per list element in both cases; the index list would require
+twice as much space if 32 bit list elements were used.
+
+.ks
+.nf
+ Index list: 1, 4, 8-11, 15, 23-39 7 words
+ Offset list: P1 P3 Z3 H4 P4 Z7 H17 7 words
+
+ Inverted I-L: 2-3, 5-7, 12-14, 16-22 8 words
+ Inverted O-L: Z1 H2 Z1 H3 Z4 H3 Z1 H7 8 words
+.fi
+.ke
+
+The bottom half of the figure shows the encodings for the inverted lists,
+i.e., the lists which would be produced by a PIX_NOT rasterop operation.
+Since both encodings reflect only the points in a line where level changes
+occur, the information content and list size of the normal and inverted
+lists are comparable. The encoding for an integer mask would be equivalent
+except for the addition of occasional SH or SL instructions to set the high
+value.
+
+.NH 3
+Line List Structure
+
+ The full line list structure consists of a header describing the line
+list entry in the PLIO descriptor, plus the LL encoding of the line, as
+illustrated below. This information is internal to the PLIO package and
+should not be used in applications code. [The LL header was generalized
+in Aug2000 to permit larger masks; see plio.h]
+
+.ks
+.nf
+ define LL_START 4
+ struct linelist {
+ short nref # number of lines pointing here
+ short blen # length of buffer containing list
+ short len # encoded linelist length, words
+ short ll[] # encoded pixel data
+ }
+.fi
+.ke
+
+The linelist encoding includes any trailing zeros, i.e., executing of the
+encoded instructions will regenerate the entire mask line.
+
+.NH 3
+Range List Structure
+
+ The range list structure provides applications programs with a list
+representation of a mask, for cases where it would be inefficient or
+inconvenient to access the mask as a pixel array. Range lists are not
+used to store masks in external storage, hence data compression is not an
+issue, nor is machine independence.
+
+.ks
+.nf
+ rtype = short|int|long
+ struct rangelist {
+ rtype x # x coordinate of first pixel
+ rtype n # number of pixels in range
+ rtype v # pixel value
+ }
+
+ int rl[3,ARB] # typical range list array decl
+.fi
+.ke
+
+The range list structure is defined in <plset.h>, e.g. (refer to the actual
+file for more reliable documentation for these definitions):
+
+.ks
+.nf
+ RL_FIRST # first data range entry in list
+ RL_LENELEM # size of each element of list (i.e., 3)
+ RL_MAXLEN # maximum range list length (arg=pl)
+
+ RL_LEN # physical length of range list (RL_LEN(rl))
+ RL_AXLEN # length of mask image line (RL_AXLEN(rl))
+ RLI_LEN # RL_LEN for rl = ptr to int
+ RLI_AXLEN # RL_AXLEN " " "
+ RLS_LEN # RL_LEN for rl = ptr to int
+ RLS_AXLEN # RL_AXLEN " " "
+
+ RL_X # use as RL_X(rl,i), rl = range list array
+ RL_N # Npix field of range list array element
+ RL_V # Value field of range list array element
+
+ RL_XOFF # offset of xstart field
+ RL_NOFF # offset of npix field
+ RL_VOFF # offset of value field
+.fi
+.ke
+
+The range list is represented as a simple two dimensional integer or
+short integer array. The first line of the two dimensional array is
+used as the \fBrange list header\fR, and is used to store the \fIlen\fR
+and \fIaxlen\fR parameters describing the encoded mask line. Each
+subsequent entry defines a range \fInpix\fR of mask image pixels,
+starting at pixel \fIx\fR (absolute pixel coordinates), all of which
+have the value \fIvalue\fR. Only nonzero ranges are stored.
+Note that the \fIlen\fR field defines the entire length of the structure,
+including the header range and data ranges.
+
+.NH 3
+Example: A Small Mask
+
+ A complete example of a small mask (75 columns by 40 lines) is shown below.
+This mask and the sample output were prepared by the PLIO debug interpreter,
+file \fIzzdebug.x\fR in the PLIO source directory.
+
+.nf
+40 .1111111..............................................................22222
+39 .1111111..............................................................22222
+38 .1111111...............................................................2222
+37 .1111111...............................................................2222
+36 .1111111............................................................4......
+35 .1111111.....................11111111111111111111111111...........4444.....
+34 .1111111.....................11111111111111111111111111........44444444....
+33 .1111111......................1111111111111111111111111......44444444......
+32 .1111111......................1111111111111111111111111...44444444.........
+31 .1111111......................1111111111111111111111111.44444444...........
+30 .1111111...........4..........1111111111111111111111115444444..............
+29 .1111111...........4..........11111111111111111111155554444................
+28 .1111111...........4..........111111111111111111155555544..................
+27 .1111111...........4..........1111111111111111555555551....................
+26 .1111111.....................11111111111111155555555111....................
+25 .1111111.....................11111111111115555555111111....................
+24 ............................1111111111155444444.1111111....................
+23 ...........................111111111155544444....111111....................
+22 ..............1...........1111111155555444........11111....................
+21 ........................1111111155555554..........11111....................
+20 ........................111111555555511...........11111....................
+19 ........................111555555551111...........11111....................
+18 ........................155555555111111...........11111....................
+17 ......................445555551111111111.........111111....................
+16 ....................444455551111111111111.......1111111....................
+15 ..................4444445111111111111111111111111111111....................
+14 ...............44444444.1111111111111111111111111111111....................
+13 .............44444444......................................................
+12 ..........44444444.........................................................
+11 ........44444444...........................................................
+10 .........4444........................22222.................................
+ 9 ..........4.........................2222222..............22222.............
+ 8 ....................................2222222.............2222222............
+ 7 ....................................2222222.............2222222............
+ 6 .....................................22222..............2222222............
+ 5 11111111111111111111.....................................22222.............
+ 4 11111111111111111111.......................................................
+ 3 11111111111111111111.......................................................
+ 2 11111111111111111111.......................................................
+ 1 11111111111111111111.......................................................
+ 123456789012345678901234567890123456789012345678901234567890123456789012345
+ 1 2 3 4 5 6 7
+.fi
+
+This first figure shows the mask itself, output graphically as a character
+matrix. This is an integer mask wherein the integer value of each pixel is
+the ASCII value of the character shown. The mask was created by \fIor\fR-in
+a number of regions together, using a different mask value for each region.
+The result in areas where the regions intersect is a new mask pixel value.
+
+The figure below shows the internal data structures used to represent the
+previous mask. Both the line list and range list representations of the
+mask are shown. The size of the packed line list is 582 words, 189 of which
+are free (no longer used due to updates), hence the packed line list size
+would be 393 words following a call to \fIpl_compress\fR to compress the mask.
+
+.nf
+ Mask 1EECD naxes=2 [75,40] maxval=177 plane=[75,40]
+ max buffered line size 1024, max actual line size 16
+ 40 lines total, 40 are nonempty, mask is nonempty
+ llbp=42AF5, len=1190, op=583, free=189, nupdates=35
+
+ Index at 1EFF1 containing 40 lines:
+ 4 4 4 4 17 26 35 35 166 177 187
+ 194 201 208 218 228 241 254 266 554 292 568
+ 319 333 347 360 492 507 523 539 423 435 447
+ 459 471 483 148 148 157 157
+
+ Line list containing 40 lines:
+ [1:4] IH48(49) H20 Z55 (75,49)
+ [5] IH48(49) H20 IH1(50) Z37 H5 Z13 (75,50)
+ [6] IH49(50) Z37 H5 Z14 H7 Z12 (75,50)
+ [7:8] IH49(50) Z36 H7 Z13 H7 Z12 (75,50)
+ [9] IH51(52) P11 DH2(50) Z25 H7 Z14 H5 Z13 (75,50)
+ [10] IH51(52) Z9 H4 DH2(50) Z24 H5 Z33 (75,50)
+ [11] IH51(52) Z8 H8 Z59 (75,52)
+ [12] IH51(52) Z10 H8 Z57 (75,52)
+ [13] IH51(52) Z13 H8 Z54 (75,52)
+ [14] IH51(52) Z15 H8 DH3(49) Z1 H31 Z20 (75,49)
+ [15] IH51(52) Z18 H6 IS1(53) DH4(49) H30 Z20 (75,49)
+ [16] IH51(52) Z20 H4 IH1(53) H4 DH4(49) H13 Z7 H7 Z20 (75,49)
+ [17] IH51(52) Z22 H2 IH1(53) H6 DH4(49) H10 Z9 H6 Z20 (75,49)
+ [18] IH48(49) P25 IH4(53) H8 DH4(49) H6 Z11 H5 Z20 (75,49)
+ [19] IH48(49) Z24 H3 IH4(53) H8 DH4(49) H4 Z11 H5 Z20 (75,49)
+ [20] IH48(49) Z24 H6 IH4(53) H7 DH4(49) H2 Z11 H5 Z20 (75,49)
+ [21] IH48(49) Z24 H8 IH4(53) H7 DS1(52) DH3(49) Z10 H5 Z20
+ (75,49)
+ [22] IH48(49) P15 Z11 H8 IH4(53) H5 DH1(52) H3 DH3(49) Z8 H5
+ Z20 (75,49)
+ [23] IH48(49) Z27 H10 IH4(53) H3 DH1(52) H5 DH3(49) Z4 H6 Z20
+ (75,49)
+ [24] IH48(49) Z28 H11 IH4(53) H2 DH1(52) H6 DH3(49) Z1 H7 Z20
+ (75,49)
+ [25] IH48(49) Z1 H7 Z21 H13 IH4(53) H7 DH4(49) H6 Z20 (75,49)
+ [26] IH48(49) Z1 H7 Z21 H15 IH4(53) H8 DH4(49) H3 Z20 (75,49)
+ [27] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H16 IH4(53) H8
+ DS4(49) Z20 (75,49)
+ [28] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H19 IH4(53) H6
+ DH1(52) H2 Z18 (75,52)
+ [29] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H21 IH4(53) H4
+ DH1(52) H4 Z16 (75,52)
+ [30] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H24 IS4(53)
+ DH1(52) H6 Z14 (75,52)
+ [31] IH48(49) Z1 H7 Z22 H25 IH3(52) Z1 H8 Z11 (75,52)
+ [32] IH48(49) Z1 H7 Z22 H25 IH3(52) Z3 H8 Z9 (75,52)
+ [33] IH48(49) Z1 H7 Z22 H25 IH3(52) Z6 H8 Z6 (75,52)
+ [34] IH48(49) Z1 H7 Z21 H26 IH3(52) Z8 H8 Z4 (75,52)
+ [35] IH48(49) Z1 H7 Z21 H26 IH3(52) Z11 H4 Z5 (75,52)
+ [36] IH48(49) Z1 H7 IH3(52) P61 Z6 (75,52)
+ [37:38] IH48(49) Z1 H7 IH1(50) Z63 H4 (75,50)
+ [39:40] IH48(49) Z1 H7 IH1(50) Z62 H5 (75,50)
+
+ Line list containing 40 lines:
+ [1:4] 1-20(49)
+ [5] 1-20(49) 58-62(50)
+ [6] 38-42(50) 57-63(50)
+ [7:8] 37-43(50) 57-63(50)
+ [9] 11(52) 37-43(50) 58-62(50)
+ [10] 10-13(52) 38-42(50)
+ [11] 9-16(52)
+ [12] 11-18(52)
+ [13] 14-21(52)
+ [14] 16-23(52) 25-55(49)
+ [15] 19-24(52) 25(53) 26-55(49)
+ [16] 21-24(52) 25-28(53) 29-41(49) 49-55(49)
+ [17] 23-24(52) 25-30(53) 31-40(49) 50-55(49)
+ [18] 25(49) 26-33(53) 34-39(49) 51-55(49)
+ [19] 25-27(49) 28-35(53) 36-39(49) 51-55(49)
+ [20] 25-30(49) 31-37(53) 38-39(49) 51-55(49)
+ [21] 25-32(49) 33-39(53) 40(52) 51-55(49)
+ [22] 15(49) 27-34(49) 35-39(53) 40-42(52) 51-55(49)
+ [23] 28-37(49) 38-40(53) 41-45(52) 50-55(49)
+ [24] 29-39(49) 40-41(53) 42-47(52) 49-55(49)
+ [25] 2-8(49) 30-42(49) 43-49(53) 50-55(49)
+ [26] 2-8(49) 30-44(49) 45-52(53) 53-55(49)
+ [27] 2-8(49) 20(52) 31-46(49) 47-54(53) 55(49)
+ [28] 2-8(49) 20(52) 31-49(49) 50-55(53) 56-57(52)
+ [29] 2-8(49) 20(52) 31-51(49) 52-55(53) 56-59(52)
+ [30] 2-8(49) 20(52) 31-54(49) 55(53) 56-61(52)
+ [31] 2-8(49) 31-55(49) 57-64(52)
+ [32] 2-8(49) 31-55(49) 59-66(52)
+ [33] 2-8(49) 31-55(49) 62-69(52)
+ [34] 2-8(49) 30-55(49) 64-71(52)
+ [35] 2-8(49) 30-55(49) 67-70(52)
+ [36] 2-8(49) 69(52)
+ [37:38] 2-8(49) 72-75(50)
+ [39:40] 2-8(49) 71-75(50)
+.fi
+
+The line list and range list tables shown consist of two columns, the first
+listing the range of mask lines pointing to the line or range list shown to
+the right (a sequence of identical mask lines will point to the same encoded
+line list). Each instruction which changes the mask pixel value is followed
+by the new current mask value in parenthesis. Zero ranges are omitted in
+the range list format. The "pl_circle" regions appear as ellipses since
+a printed character does not have a unit aspect ratio.
+
+.NH 3
+Pixel List Descriptor
+
+ For runtime access to a mask we require, for an N dimensional mask, an
+N-1 dimensional array of line list pointers, plus the line lists themselves.
+Multiple line list pointers may point to the same encoded line list.
+All line pointers are valid at all times, i.e., NULL pointers are not
+permitted, even for empty mask lines. Initially, all line pointers will
+point to the same entry, the encoded line list representation of an empty
+line, i.e., a line of zeros (i.e., the single instruction ZN where N=axlen[1]).
+
+.ks
+.nf
+ struct pldes {
+ int pl_magic # magic / version no.
+ int pl_private1 # used by PMIO (ref_im)
+ int pl_private2 # used by PMIO (mapxy flag)
+ int pl_maxline # max encoded line lentgh
+ int pl_maxval # mask depth as max pixel value
+ int pl_naxes # number of axes
+ int pl_axlen[7] # axis lengths
+ int pl_plane[7] # current plane for pl_setplane
+ int pl_llbp # line list bufptr
+ int pl_llop # next location in llbuf
+ int pl_lllen # current llbuf length
+ int pl_llfree # amount of free space in list
+ int pl_llnupdates # line list has been modified
+ int pl_llinc # llbuf increment on overflow
+ int pl_nlp # number of line pointers
+ int pl_lp[] # array of line pointers
+ }
+.fi
+.ke
+
+The main descriptor, which is a dynamically allocated data structure, is a
+fixed size descriptor, the size of which depends upon the dimensionality and
+size of the mask. A single additional dynamically allocated buffer of type
+\fIshort\fR is used to store the encoded line lists. The size of this buffer
+may vary at runtime as the mask is edited. New line lists, or edited line
+lists which increase in size, are inserted at the end of the buffer.
+As existing line lists are freed a count of the amount of free space is
+kept in \fIpl_llfree\fR. If the percent of free space reaches a predetermined
+level garbage collection is possible by copying the list, otherwise the line
+list buffer is reallocated to increase its size. The line pointers \fIpl_lp\fR
+are actually offsets into \fIllbuf\fR, rather than true pointers.
+
+.NH 3
+External Storage Format
+
+ A mask is stored externally as a variable length array of 32 bit MII
+integers (the stored mask header) followed by two variable length arrays of
+16 bit MII integers, packed in the following format:
+
+.ks
+.nf
+ struct pl_extern {
+ int ple_magic # magic / version no.
+ int ple_naxes # mask dimensionality
+ int ple_axlen[7] # length of each axis
+ int ple_llop # output pointer into llbuf
+ int ple_lllen # llbuf length, words
+ int ple_nlp # number of line pointers (lines)
+ int ple_nlpx # length of compressed index
+ int ple_exlen # length of full pl_extern struct
+ int ple_flags # flag bits
+ int ple_maxline # saved pl_maxline
+ int ple_maxval # saved pl_maxval
+ short ple_pkindex[] # packed line list index array
+ short ple_llbuf[] # line list buffer
+ }
+.fi
+.ke
+
+The \fIpl_compress\fR function is applied before a mask is encoded
+into the external storage format, to eliminate the unused space in the line
+list buffer which occurs during dynamic updates to the runtime list structure.
+The line list index, containing one integer entry for each line in the mask
+in the runtime format, is compressed using \fIpl_p2li\fR, storing in the
+external representation the compressed array encoded as a line list in
+\fIple_pkindex\fR. This is especially important for large masks, as otherwise
+the index array could be by far the biggest contributor to the size of the
+mask when encoded into its external format.
+
+No format conversions are required other than decoding the compressed line list
+index and possibly byte swapping, hence accessing a stored list is very fast.
+Further data compression is possible when packing the list for storage, but it
+is not clear if this is justified.
diff --git a/sys/plio/README b/sys/plio/README
new file mode 100644
index 00000000..440bf528
--- /dev/null
+++ b/sys/plio/README
@@ -0,0 +1,288 @@
+PMIO -- The Pixel Mask I/O package (PLIO for image masks).
+PLIO -- The Pixel List I/O package (no ties to IMIO)
+
+ A PIXEL LIST is a compressed, region oriented data structure used to store
+an image matrix. The pixel list package is used to create, manage, and access
+this data structure. Although the PLIO package can stand alone and is useful
+in its own right, one of the main uses of the pixel list package is in the IMIO
+interface, which can access a pixel list as if it were a MASK IMAGE.
+See PLIO.hlp for further information on the PLIO package and image masks.
+
+The pixel list package itself does not support any fancy image coordinate
+transformations. If an image has an associated pixel mask, the pixel mask
+refers to the physical image matrix. An application written at the IMIO level
+where an image section transformation may be defined for an image should
+normally use the PMIO (pixel mask) package in preference to PLIO. PMIO is
+equivalent to PLIO, except that coordinates are input in image section
+coordinates, and a reference image is used to map such coordinates onto the
+physical image matrix.
+
+
+1. IMIO Mask Image Interface
+
+ im = im_pmmap (maskname, mode, ref_im|NULL)
+ im = im_pmmapo (pm, ref_im)
+
+ imseti (im, IM_RLIO, YES|NO) # enable range list i/o
+ imseti (im, IM_PMDES, pm) # inquire PM descriptor
+ pm = imstati (im, IM_PMDES)
+
+ bool = im_pmlne[123] (im[, lineno[, bandno]])
+ bool = im_pmsne[123] (im, x1, x2[, y1, y2[, z1, z2]])
+ bool = im_pmlnev (im, v)
+ bool = im_pmsnev (im, vs, ve, ndim)
+
+ mp = mio_open (maskname, flags, im) # Masked Image I/O
+ mp = mio_openo (pm, im)
+ value = mio_stati (mp, param)
+ mio_seti (mp, param, value)
+ mio_setrange (mp, vs, ve, ndim)
+ n|EOF = mio_[gp]lseg[silrdx] (mp, ptr, mval, v, npix)
+ mio_close (mp)
+
+
+2. Pixel Mask Interface (uses reference image for section transformation)
+
+ pm = pm_newmask (ref_im, depth)
+
+ pm = pm_open (bufptr|NULL)
+ pm = pm_create (naxes, axlen, depth)
+ pm = pm_newcopy (pm)
+ pm_close (pm)
+
+ pm_[sg]size (pm, naxes, alxen, depth)
+ pm_seti (pm, param, value)
+ value = pm_stati (pm, param)
+ pm_debug (pm, outfd, maxcol, flags)
+ bool = pm_empty (pm)
+ pm_compress (pm)
+ pm_clear (pm)
+
+ pm_load (pm, bufptr)
+ nwords = pm_save (pm, bufptr, buflen, save_flags)
+ pm_loadf (pm, fname, title, maxch)
+ pm_savef (pm, fname, title, save_flags)
+ pm_[load|save]im (pm, imname[, save_flags])
+
+ ptr = pm_emptyline (pm)
+ ptr = pm_access (pm, v)
+ bool = pm_linenotempty (pm, v)
+ bool = pm_sectnotempty (pm, vs, ve, ndim)
+ pm[gp]l[lrp][sil] (pm, v, buf, b_depth, npix, rop)
+
+ pm_[set|get]plane (pm, v)
+ pm_point (pm, x, y, rop)
+ pm_circle (pm, x, y, r, rop)
+ pm_box (pm, x1,y1, x2,y2, rop)
+ pm_line (pm, x1,y1, x2,y2, width, rop)
+ pm_polygon (pm, x, y, npts, rop)
+
+ pm_rop (pm_src, vs, pm_dst, vs, vn, rop)
+ pm_stencil (pm_src, vs, pm_dst, vs, pm_stl, vs, vn, rop)
+
+
+2.1 Random Access to a Pixel Mask
+
+ pmr = pmr_open (pm, plane, buflimit)
+ pmr_setrect (pmr, x1,y1, x2,y2)
+ mval = pmr_getpix (pmr, i, j)
+ pmr_close (pmr)
+
+
+3. Pixel List Interface (stands alone; independent of IMIO; no coord xforms)
+
+ pl = pl_open (bufptr|NULL)
+ pl = pl_create (naxes, axlen, depth)
+ pl = pl_newcopy (pl)
+ pl_close (pl)
+
+ pl_[sg]size (pl, naxes, axlen, depth)
+ pl_seti (pl, param, value)
+ value = pl_stati (pl, param)
+ pl_debug (pl, outfd, maxcol, flags)
+ bool = pl_empty (pl)
+ pl_compress (pl)
+ pl_clear (pl)
+
+ pl_load (pl, bufptr)
+ nwords = pl_save (pl, bufptr, buflen, save_flags)
+ pl_loadf (pl, fname, title, maxch)
+ pl_savef (pl, fname, title, save_flags)
+ pl_[load|save]im (pl, imname[, save_flags])
+
+ ptr = pl_emptyline (pl)
+ ptr = pl_access (pl, v)
+ bool = pl_linenotempty (pl, v)
+ bool = pl_sectnotempty (pl, vs, ve, ndim)
+ pl[gp]l[lrp][sil] (pl, v, buf, b_depth, npix, rop)
+
+ pl_[set|get]plane (pl, v)
+ pl_point (pl, x, y, rop)
+ pl_circle (pl, x, y, r, rop)
+ pl_box (pl, x1,y1, x2,y2, rop)
+ pl_line (pl, x1,y1, x2,y2, width, rop)
+ pl_polygon (pl, x, y, npts, rop)
+
+ pl_rop (pl_src, vs, pl_dst, vs, vn, rop)
+ pl_stencil (pl_src, vs, pl_dst, vs, pl_stl, vs, vn, rop)
+
+
+3.1 Random Access to a Pixel List
+
+ plr = plr_open (pl, plane, buflimit)
+ plr_setrect (plr, x1,y1, x2,y2)
+ mval = plr_getpix (plr, i, j)
+ plr_getlut (plr, bufp, xsize,ysize, xblock,yblock)
+ plr_close (plr)
+
+
+3.2 Pixel, Line, and Range List Routines
+
+ pl_pixrop (px_src, xs, src_maxval,
+ px_dst, ds, dst_maxval, npix, rop)
+ pl_linerop (ll_src, xs, src_maxval,
+ ll_dst, ds, dst_maxval, ll_out, npix, rop)
+ pl_rangerop (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+ pl_linestencil (ll_src, xs, src_maxval, ll_dst, ds, dst_maxval,
+ ll_stn, xs, ll_out, npix, rop)
+
+ n = pl_[lrp]2[lrp][sil] (op_src, xs, op_dst, npix)
+
+ len = pl_llen (ll)
+
+
+4. EXAMPLE
+
+4.1 Sample Mask (pl_draw output)
+
+ 40 .1111111..............................................................22222
+ 39 .1111111..............................................................22222
+ 38 .1111111...............................................................2222
+ 37 .1111111...............................................................2222
+ 36 .1111111............................................................4......
+ 35 .1111111.....................11111111111111111111111111...........4444.....
+ 34 .1111111.....................11111111111111111111111111........44444444....
+ 33 .1111111......................1111111111111111111111111......44444444......
+ 32 .1111111......................1111111111111111111111111...44444444.........
+ 31 .1111111......................1111111111111111111111111.44444444...........
+ 30 .1111111...........4..........1111111111111111111111115444444..............
+ 29 .1111111...........4..........11111111111111111111155554444................
+ 28 .1111111...........4..........111111111111111111155555544..................
+ 27 .1111111...........4..........1111111111111111555555551....................
+ 26 .1111111.....................11111111111111155555555111....................
+ 25 .1111111.....................11111111111115555555111111....................
+ 24 ............................1111111111155444444.1111111....................
+ 23 ...........................111111111155544444....111111....................
+ 22 ..............1...........1111111155555444........11111....................
+ 21 ........................1111111155555554..........11111....................
+ 20 ........................111111555555511...........11111....................
+ 19 ........................111555555551111...........11111....................
+ 18 ........................155555555111111...........11111....................
+ 17 ......................445555551111111111.........111111....................
+ 16 ....................444455551111111111111.......1111111....................
+ 15 ..................4444445111111111111111111111111111111....................
+ 14 ...............44444444.1111111111111111111111111111111....................
+ 13 .............44444444......................................................
+ 12 ..........44444444.........................................................
+ 11 ........44444444...........................................................
+ 10 .........4444........................22222.................................
+ 9 ..........4.........................2222222..............22222.............
+ 8 ....................................2222222.............2222222............
+ 7 ....................................2222222.............2222222............
+ 6 .....................................22222..............2222222............
+ 5 11111111111111111111.....................................22222.............
+ 4 11111111111111111111.......................................................
+ 3 11111111111111111111.......................................................
+ 2 11111111111111111111.......................................................
+ 1 11111111111111111111.......................................................
+ 123456789012345678901234567890123456789012345678901234567890123456789012345
+ 1 2 3 4 5 6 7
+
+
+4.2 Sample Debug Output (for above mask)
+
+Mask 1EECD naxes=2 [75,40] maxval=177 plane=[75,40]
+max buffered line size 1024, max actual line size 16
+40 lines total, 40 are nonempty, mask is nonempty
+llbp=42AF5, len=1190, op=583, free=189, nupdates=35
+Index at 1EFF1 containing 40 lines:
+ 4 4 4 4 17 26 35 35 166 177 187
+ 194 201 208 218 228 241 254 266 554 292 568
+ 319 333 347 360 492 507 523 539 423 435 447
+ 459 471 483 148 148 157 157
+Line list containing 40 lines:
+[1:4] IH48(49) H20 Z55 (75,49)
+[5] IH48(49) H20 IH1(50) Z37 H5 Z13 (75,50)
+[6] IH49(50) Z37 H5 Z14 H7 Z12 (75,50)
+[7:8] IH49(50) Z36 H7 Z13 H7 Z12 (75,50)
+[9] IH51(52) P11 DH2(50) Z25 H7 Z14 H5 Z13 (75,50)
+[10] IH51(52) Z9 H4 DH2(50) Z24 H5 Z33 (75,50)
+[11] IH51(52) Z8 H8 Z59 (75,52)
+[12] IH51(52) Z10 H8 Z57 (75,52)
+[13] IH51(52) Z13 H8 Z54 (75,52)
+[14] IH51(52) Z15 H8 DH3(49) Z1 H31 Z20 (75,49)
+[15] IH51(52) Z18 H6 IS1(53) DH4(49) H30 Z20 (75,49)
+[16] IH51(52) Z20 H4 IH1(53) H4 DH4(49) H13 Z7 H7 Z20 (75,49)
+[17] IH51(52) Z22 H2 IH1(53) H6 DH4(49) H10 Z9 H6 Z20 (75,49)
+[18] IH48(49) P25 IH4(53) H8 DH4(49) H6 Z11 H5 Z20 (75,49)
+[19] IH48(49) Z24 H3 IH4(53) H8 DH4(49) H4 Z11 H5 Z20 (75,49)
+[20] IH48(49) Z24 H6 IH4(53) H7 DH4(49) H2 Z11 H5 Z20 (75,49)
+[21] IH48(49) Z24 H8 IH4(53) H7 DS1(52) DH3(49) Z10 H5 Z20 (75,49)
+[22] IH48(49) P15 Z11 H8 IH4(53) H5 DH1(52) H3 DH3(49) Z8 H5 Z20 (75,49)
+[23] IH48(49) Z27 H10 IH4(53) H3 DH1(52) H5 DH3(49) Z4 H6 Z20 (75,49)
+[24] IH48(49) Z28 H11 IH4(53) H2 DH1(52) H6 DH3(49) Z1 H7 Z20 (75,49)
+[25] IH48(49) Z1 H7 Z21 H13 IH4(53) H7 DH4(49) H6 Z20 (75,49)
+[26] IH48(49) Z1 H7 Z21 H15 IH4(53) H8 DH4(49) H3 Z20 (75,49)
+[27] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H16 IH4(53) H8 DS4(49) Z20
+ (75,49)
+[28] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H19 IH4(53) H6 DH1(52) H2 Z18
+ (75,52)
+[29] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H21 IH4(53) H4 DH1(52) H4 Z16
+ (75,52)
+[30] IH48(49) Z1 H7 IH3(52) P12 DH3(49) Z10 H24 IS4(53) DH1(52) H6 Z14
+ (75,52)
+[31] IH48(49) Z1 H7 Z22 H25 IH3(52) Z1 H8 Z11 (75,52)
+[32] IH48(49) Z1 H7 Z22 H25 IH3(52) Z3 H8 Z9 (75,52)
+[33] IH48(49) Z1 H7 Z22 H25 IH3(52) Z6 H8 Z6 (75,52)
+[34] IH48(49) Z1 H7 Z21 H26 IH3(52) Z8 H8 Z4 (75,52)
+[35] IH48(49) Z1 H7 Z21 H26 IH3(52) Z11 H4 Z5 (75,52)
+[36] IH48(49) Z1 H7 IH3(52) P61 Z6 (75,52)
+[37:38] IH48(49) Z1 H7 IH1(50) Z63 H4 (75,50)
+[39:40] IH48(49) Z1 H7 IH1(50) Z62 H5 (75,50)
+
+Line list containing 40 lines:
+[1:4] 1-20(49)
+[5] 1-20(49) 58-62(50)
+[6] 38-42(50) 57-63(50)
+[7:8] 37-43(50) 57-63(50)
+[9] 11(52) 37-43(50) 58-62(50)
+[10] 10-13(52) 38-42(50)
+[11] 9-16(52)
+[12] 11-18(52)
+[13] 14-21(52)
+[14] 16-23(52) 25-55(49)
+[15] 19-24(52) 25(53) 26-55(49)
+[16] 21-24(52) 25-28(53) 29-41(49) 49-55(49)
+[17] 23-24(52) 25-30(53) 31-40(49) 50-55(49)
+[18] 25(49) 26-33(53) 34-39(49) 51-55(49)
+[19] 25-27(49) 28-35(53) 36-39(49) 51-55(49)
+[20] 25-30(49) 31-37(53) 38-39(49) 51-55(49)
+[21] 25-32(49) 33-39(53) 40(52) 51-55(49)
+[22] 15(49) 27-34(49) 35-39(53) 40-42(52) 51-55(49)
+[23] 28-37(49) 38-40(53) 41-45(52) 50-55(49)
+[24] 29-39(49) 40-41(53) 42-47(52) 49-55(49)
+[25] 2-8(49) 30-42(49) 43-49(53) 50-55(49)
+[26] 2-8(49) 30-44(49) 45-52(53) 53-55(49)
+[27] 2-8(49) 20(52) 31-46(49) 47-54(53) 55(49)
+[28] 2-8(49) 20(52) 31-49(49) 50-55(53) 56-57(52)
+[29] 2-8(49) 20(52) 31-51(49) 52-55(53) 56-59(52)
+[30] 2-8(49) 20(52) 31-54(49) 55(53) 56-61(52)
+[31] 2-8(49) 31-55(49) 57-64(52)
+[32] 2-8(49) 31-55(49) 59-66(52)
+[33] 2-8(49) 31-55(49) 62-69(52)
+[34] 2-8(49) 30-55(49) 64-71(52)
+[35] 2-8(49) 30-55(49) 67-70(52)
+[36] 2-8(49) 69(52)
+[37:38] 2-8(49) 72-75(50)
+[39:40] 2-8(49) 71-75(50)
diff --git a/sys/plio/mkpkg b/sys/plio/mkpkg
new file mode 100644
index 00000000..deb49757
--- /dev/null
+++ b/sys/plio/mkpkg
@@ -0,0 +1,94 @@
+# Make the PLIO package library.
+
+$checkout libex.a lib$
+$update libex.a
+$checkin libex.a lib$
+$exit
+
+tfiles:
+ $set GFLAGS = "-k -t sil -p tf/"
+ $ifolder (tf/plglpi.x, plglp.gx) $generic $(GFLAGS) plglp.gx $endif
+ $ifolder (tf/plglri.x, plglr.gx) $generic $(GFLAGS) plglr.gx $endif
+ $ifolder (tf/pll2pi.x, pll2p.gx) $generic $(GFLAGS) pll2p.gx $endif
+ $ifolder (tf/pll2ri.x, pll2r.gx) $generic $(GFLAGS) pll2r.gx $endif
+ $ifolder (tf/plp2li.x, plp2l.gx) $generic $(GFLAGS) plp2l.gx $endif
+ $ifolder (tf/plp2ri.x, plp2r.gx) $generic $(GFLAGS) plp2r.gx $endif
+ $ifolder (tf/plplpi.x, plplp.gx) $generic $(GFLAGS) plplp.gx $endif
+ $ifolder (tf/plplri.x, plplr.gx) $generic $(GFLAGS) plplr.gx $endif
+ $ifolder (tf/plpropi.x, plprop.gx) $generic $(GFLAGS) plprop.gx $endif
+ $ifolder (tf/plr2li.x, plr2l.gx) $generic $(GFLAGS) plr2l.gx $endif
+ $ifolder (tf/plr2pi.x, plr2p.gx) $generic $(GFLAGS) plr2p.gx $endif
+ $ifolder (tf/plrropi.x, plrrop.gx) $generic $(GFLAGS) plrrop.gx $endif
+ $ifolder (tf/plrpri.x, plrpr.gx) $generic $(GFLAGS) plrpr.gx $endif
+ $ifolder (tf/plreqi.x, plreq.gx) $generic $(GFLAGS) plreq.gx $endif
+ ;
+
+zzdebug:
+zzdebug.e:
+ $set XFLAGS = "$(XFLAGS) -q"
+ $omake zzdebug.x <error.h> <ctype.h> <fset.h> <plset.h> <plio.h>
+ $link -z zzdebug.o -o zzdebug.e
+ ;
+
+libex.a:
+ # Retranslate any recently modified generic sources.
+ $ifeq (hostid, unix)
+ $call tfiles
+ $endif
+
+ @tf # Update datatype expanded files.
+
+ placcess.x <plio.h> <plset.h>
+ plalloc.x <plio.h>
+ plascii.x <ctype.h> <plset.h>
+ plbox.x <plio.h> <plset.h> plbox.h
+ plubox.x <plio.h> <plset.h> plbox.h
+ plcircle.x <plio.h> <plset.h> plcircle.h
+ plucircle.x <plio.h> <plset.h> plcircle.h
+ plclear.x <plio.h>
+ plclose.x <plio.h>
+ plcmpress.x <plio.h>
+ plcompare.x <plio.h> <plset.h>
+ plcreate.x
+ pldbgout.x
+ pldebug.x <plio.h> <plset.h>
+ plempty.x <plio.h>
+ plemptyline.x <plio.h>
+ plglls.x <plio.h>
+ plgplane.x <plio.h>
+ plgsize.x <plio.h> <plset.h>
+ pllen.x <plio.h>
+ plleq.x <plio.h>
+ plline.x <math.h> <plio.h> <plset.h>
+ pllinene.x <plio.h> <plset.h>
+ pllnext.x pllseg.h <plio.h>
+ plload.x <plio.h> <plset.h>
+ plloadf.x <plio.h> <plset.h>
+ plloadim.x <imhdr.h> <imset.h> <plio.h> <plset.h>
+ plloop.x <plio.h>
+ pllpr.x <plio.h>
+ pllrop.x <plio.h> <plset.h> pllseg.h
+ pllsten.x <plio.h> <plset.h> pllseg.h
+ plnewcopy.x <plio.h> <plset.h>
+ plopen.x <plio.h> <plset.h>
+ plplls.x <plio.h>
+ plpoint.x <plio.h> <plset.h>
+ plpolygon.x <plio.h> <plset.h> plpolygon.h
+ plupolygon.x <plio.h> <plset.h> plpolygon.h
+ plregrop.x <plio.h> <plset.h>
+ plrio.x <plio.h>
+ plrop.x <plio.h> <plset.h>
+ plsave.x <plio.h> <plset.h>
+ plsavef.x <plio.h> <plset.h>
+ plsaveim.x <imhdr.h> <imset.h> <mach.h> <plio.h> <plset.h>
+ plsectnc.x <plio.h> pllseg.h <plset.h>
+ plsectne.x <plio.h> pllseg.h <plset.h>
+ plseti.x <plio.h> <plset.h>
+ plsplane.x <plio.h>
+ plssize.x <plio.h> <plset.h>
+ plsslv.x <plio.h> <plset.h>
+ plstati.x <plio.h> <plset.h>
+ plsten.x <plio.h> <plset.h>
+ plupdate.x <mach.h> <plio.h> <plset.h>
+ plvalid.x <plio.h>
+ ;
diff --git a/sys/plio/placcess.x b/sys/plio/placcess.x
new file mode 100644
index 00000000..92cb036d
--- /dev/null
+++ b/sys/plio/placcess.x
@@ -0,0 +1,59 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_ACCESS -- Return a pointer (type short) to the encoded line list data
+# for the indicated mask image line. A valid pointer is always returned;
+# if the mask line is empty, the pointer will point to "empty line" linelist.
+
+pointer procedure pl_access (pl, v)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I coordinates of desired line
+
+int pl_reference()
+
+begin
+ return (Ref (pl, pl_reference(pl,v)))
+end
+
+
+# PL_REFERENCE -- Return a reference (llbuf offset) to the indicated mask
+# image line. A valid offset is always returned; if the mask line is empty,
+# the offset will be that of the "empty line" linelist.
+
+int procedure pl_reference (pl, v)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I coordinates of desired line
+
+int index, i
+int totlen, axlen
+define oob_ 91
+
+begin
+ # Compute the index of the line in the line pointer array.
+ if (PL_NAXES(pl) == 2) {
+ # Optimized for case naxes=2.
+ index = v[2]
+ if (index < 1 || index > PL_AXLEN(pl,2))
+ goto oob_
+ } else {
+ # General case.
+ index = 1
+ totlen = 1
+ do i = 2, PL_NAXES(pl) {
+ axlen = PL_AXLEN(pl,i)
+ if (v[i] < 1 || v[i] > axlen)
+ goto oob_
+ index = index + totlen * (v[i] - 1)
+ totlen = totlen * axlen
+ }
+ }
+
+ return (PL_LP(pl,index))
+oob_
+ call syserr (SYS_PLREFOOB)
+end
diff --git a/sys/plio/plalloc.x b/sys/plio/plalloc.x
new file mode 100644
index 00000000..1fecf486
--- /dev/null
+++ b/sys/plio/plalloc.x
@@ -0,0 +1,39 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_ALLOC -- Allocate space in the line list buffer, returning the llbuf
+# offset of the allocated area as the function value. If overflow occurs
+# the buffer is resized.
+
+int procedure pl_alloc (pl, nwords)
+
+pointer pl #I mask descriptor
+int nwords #I number of words of storage to allocate
+
+int newbuf
+int len, o_len, inc, op
+errchk realloc
+
+begin
+ len = PL_LLLEN(pl) # current buffer length
+ inc = PL_LLINC(pl) # length increment
+ op = PL_LLOP(pl) # next available location
+
+ newbuf = op
+ op = newbuf + nwords
+
+ for (o_len = len; op >= len; ) {
+ inc = min (PL_MAXINC, inc * 2)
+ len = len + inc
+ }
+
+ if (len != o_len)
+ call realloc (PL_LLBP(pl), len, TY_SHORT)
+
+ PL_LLLEN(pl) = len
+ PL_LLINC(pl) = inc
+ PL_LLOP(pl) = op
+
+ return (newbuf)
+end
diff --git a/sys/plio/plascii.x b/sys/plio/plascii.x
new file mode 100644
index 00000000..8106b10e
--- /dev/null
+++ b/sys/plio/plascii.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <plset.h>
+
+# PL_ASCIIDUMP -- Dump a two dimensional region of a mask as a printable ASCII
+# character array on the output stream. Intended as a simple debugging tool;
+# see also PL_SAVEIM.
+
+procedure pl_asciidump (pl, vs, ve, outfd)
+
+pointer pl #I mask descriptor
+long vs[ARB] #I ll vector (only first two elements used)
+long ve[ARB] #I ur vector (only first two elements used)
+int outfd #I output file
+
+pointer sp, pv, cv
+int npix, ch, i
+long v[PL_MAXDIM]
+errchk pl_valid
+
+begin
+ call pl_valid (pl)
+ npix = ve[1] - vs[1] + 1
+
+ call smark (sp)
+ call salloc (pv, npix, TY_INT)
+ call salloc (cv, npix, TY_CHAR)
+
+ # Output mask.
+ call amovl (vs, v, PL_MAXDIM)
+ v[2] = ve[2]
+
+ while (v[2] >= vs[2]) {
+ call pl_glpi (pl, v, Memi[pv], 0, npix, PIX_SRC)
+ do i = 1, npix {
+ ch = mod (Memi[pv+i-1], 128)
+ if (ch < 32)
+ ch = ch + 32
+ if (ch <= 32 || ch == 127)
+ ch = '.'
+ Memc[cv+i-1] = ch
+ }
+ call fprintf (outfd, "%3d ")
+ call pargi (v[2])
+ call write (outfd, Memc[cv], npix)
+ call putci (outfd, '\n')
+ v[2] = v[2] - 1
+ }
+
+ # Label the columns.
+ call fprintf (outfd, "%5t")
+ do i = 1, npix
+ call putci (outfd, TO_DIGIT(mod (i,10)))
+ call fprintf (outfd, "\n")
+
+ call fprintf (outfd, "%5t")
+ do i = 1, npix
+ if (mod (i, 10) == 0)
+ call putci (outfd, TO_DIGIT(i / 10))
+ else
+ call putci (outfd, ' ')
+ call fprintf (outfd, "\n")
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plbox.h b/sys/plio/plbox.h
new file mode 100644
index 00000000..f988b9d6
--- /dev/null
+++ b/sys/plio/plbox.h
@@ -0,0 +1,10 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LEN_BOXDES 6
+define B_PL Memi[$1] # reference mask
+define B_X1 Memi[$1+1] # X1 coord of box
+define B_Y1 Memi[$1+2] # Y1 coord of box
+define B_X2 Memi[$1+3] # X2 coord of box
+define B_Y2 Memi[$1+4] # Y2 coord of box
+define B_PV Memi[$1+5] # pixel value
+
diff --git a/sys/plio/plbox.x b/sys/plio/plbox.x
new file mode 100644
index 00000000..35dbda4c
--- /dev/null
+++ b/sys/plio/plbox.x
@@ -0,0 +1,37 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plbox.h"
+
+
+# PL_BOX -- Rasterop between a box as source, and an existing mask as dest.
+# This is a 2-dim operator. The pl_setplane procedure is used to specify
+# the plane to be modified.
+
+procedure pl_box (pl, x1,y1, x2,y2, rop)
+
+pointer pl #I mask descriptor
+int x1,y1 #I lower left corner of box
+int x2,y2 #I upper right corner of box
+int rop #I rasterop
+
+pointer sp, ufd
+extern pl_ubox()
+
+begin
+ call plvalid (pl)
+ call smark (sp)
+ call salloc (ufd, LEN_BOXDES, TY_STRUCT)
+
+ B_PL(ufd) = pl
+ B_X1(ufd) = max(1, min(PL_AXLEN(pl,1), x1))
+ B_Y1(ufd) = max(1, min(PL_AXLEN(pl,2), y1))
+ B_X2(ufd) = max(1, min(PL_AXLEN(pl,1), x2))
+ B_Y2(ufd) = max(1, min(PL_AXLEN(pl,2), y2))
+ B_PV(ufd) = 1
+
+ call pl_regionrop (pl, pl_ubox, ufd, y1, y2, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plcircle.h b/sys/plio/plcircle.h
new file mode 100644
index 00000000..f77492f9
--- /dev/null
+++ b/sys/plio/plcircle.h
@@ -0,0 +1,10 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+
+define LEN_CIRCLEDES 5
+define C_PL Memi[$1] # reference mask
+define C_XCEN Memr[P2R($1+1)] # X1 coord of circle
+define C_YCEN Memr[P2R($1+2)] # Y1 coord of circle
+define C_RADIUS Memr[P2R($1+3)] # X2 coord of circle
+define C_PV Memi[$1+4] # pixel value
+
diff --git a/sys/plio/plcircle.x b/sys/plio/plcircle.x
new file mode 100644
index 00000000..504c1ce5
--- /dev/null
+++ b/sys/plio/plcircle.x
@@ -0,0 +1,43 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plcircle.h"
+
+
+# PL_CIRCLE -- Rasterop between a circular region as source, and an existing
+# mask as destination. It is not necessary for the center of the circle to
+# be inside the mask; if it is outside, the boundary of the circle will be
+# clipped to the boundary of the mask. This is a 2-dim operator. If the
+# image dimensionality is greater than two the pl_setplane procedure should
+# be called first to specify the plane to be modified.
+
+procedure pl_circle (pl, x, y, radius, rop)
+
+pointer pl #I mask descriptor
+int x,y #I center coords of circle
+int radius #I radius of circle
+int rop #I rasterop
+
+int y1, y2
+pointer sp, ufd
+extern pl_ucircle()
+
+begin
+ call plvalid (pl)
+ call smark (sp)
+ call salloc (ufd, LEN_CIRCLEDES, TY_STRUCT)
+
+ y1 = max ( 1, min (PL_AXLEN(pl,2), y - radius))
+ y2 = max (y1, min (PL_AXLEN(pl,2), y + radius))
+
+ C_PL(ufd) = pl
+ C_XCEN(ufd) = x
+ C_YCEN(ufd) = y
+ C_RADIUS(ufd) = radius
+ C_PV(ufd) = 1
+
+ call pl_regionrop (pl, pl_ucircle, ufd, y1, y2, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plclear.x b/sys/plio/plclear.x
new file mode 100644
index 00000000..5ece1b5d
--- /dev/null
+++ b/sys/plio/plclear.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_CLEAR -- Clear a mask. The entire surface is cleared. This is equivalent
+# to a full surface pl_rop with rop=PIX_CLR, but is more convenient and can be
+# implemented more efficiently since the entire surface is cleared.
+
+procedure pl_clear (pl)
+
+pointer pl #I mask descriptor
+
+pointer lp
+int n_len, i
+errchk realloc
+
+begin
+ # Clear the line list buffer.
+ lp = Ref (pl, PL_EMPTYLINE)
+ PL_LLOP(pl) = LP_BLEN(lp)
+ LP_NREFS(lp) = PL_NLP(pl)
+
+ do i = 1, PL_NLP(pl)
+ PL_LP(pl,i) = PL_EMPTYLINE
+
+ n_len = PL_LLBUFLEN
+ call realloc (PL_LLBP(pl), n_len, TY_SHORT)
+
+ PL_LLLEN(pl) = n_len
+ PL_LLFREE(pl) = 0
+ PL_LLNUPDATES(pl) = 0
+end
diff --git a/sys/plio/plclose.x b/sys/plio/plclose.x
new file mode 100644
index 00000000..e5e53e2b
--- /dev/null
+++ b/sys/plio/plclose.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plio.h>
+
+# PL_CLOSE -- Close a mask descriptor. The memory resident mask is destroyed;
+# an explicit call to one of the save procedures is required to save the mask
+# in external storage.
+
+procedure pl_close (pl)
+
+pointer pl #I mask descriptor
+errchk syserr
+
+begin
+ if (pl != NULL) {
+ if (PL_MAGIC(pl) != PL_MAGICVAL)
+ call syserr (SYS_PLINVDES)
+
+ if (PL_LPP(pl) != NULL)
+ call mfree (PL_LPP(pl), TY_INT)
+ if (PL_LLBP(pl) != NULL)
+ call mfree (PL_LLBP(pl), TY_SHORT)
+ call mfree (pl, TY_STRUCT)
+ }
+end
diff --git a/sys/plio/plcmpress.x b/sys/plio/plcmpress.x
new file mode 100644
index 00000000..54850a7a
--- /dev/null
+++ b/sys/plio/plcmpress.x
@@ -0,0 +1,90 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plio.h>
+
+# PL_COMPRESS -- Compress the line list buffer to eliminate any unusable
+# space, as is generated when mask lines are modified and the old line lists
+# are freed. The compress operation may move the line lists about, possibly
+# changing the line offsets.
+
+procedure pl_compress (pl)
+
+pointer pl #I mask descriptor
+
+pointer n_bp, o_lp, n_lp, op
+int nwords, r_len, b_len, i
+errchk malloc, mfree, syserr
+
+begin
+ # Redundant calls are ignored.
+ if (PL_LLNUPDATES(pl) <= 0)
+ return
+
+ # Return if there was a prior error with plalloc.
+ if (PL_LLBP(pl) == NULL)
+ call syserr (SYS_PLBADMASK)
+
+ # Count the total space in the active line lists.
+ nwords = 0
+ for (i=0; i < PL_LLOP(pl); i=i+b_len) {
+ o_lp = Ref (pl, i)
+ b_len = LP_BLEN(o_lp)
+ if (b_len <= 0 || b_len > PL_LLOP(pl))
+ call syserr (SYS_PLBADMASK)
+ if (i == PL_EMPTYLINE || LP_NREF(o_lp) > 0)
+ nwords = nwords + LP_LEN(o_lp)
+ }
+
+ # Verify that the free space accounting is correct.
+ if (nwords != (PL_LLOP(pl) - PL_LLFREE(pl)))
+ call eprintf ("Warning: PL_LLFREE inconsistent (recoverable)\n")
+
+ # Allocate a new buffer large enough to hold the compressed line list.
+ call malloc (n_bp, nwords, TY_SHORT)
+
+ # Copy the active line lists to the new buffer; as each line is
+ # copied, overwrite a couple words of the old line list with the
+ # new offset of the line.
+
+ op = 0
+ for (i=0; i < PL_LLOP(pl); i=i+b_len) {
+ o_lp = Ref (pl, i)
+ b_len = LP_BLEN(o_lp)
+ if (b_len <= 0 || b_len > PL_LLOP(pl))
+ call syserr (SYS_PLBADMASK)
+
+ if (i == PL_EMPTYLINE || LP_NREF(o_lp) > 0) {
+ n_lp = n_bp + op
+ r_len = LP_LEN(o_lp)
+
+ # The following should not be possible, barring a bug.
+ if (op + r_len > nwords)
+ call fatal (pl, "pl_compress: llbuf overflow")
+
+ call amovs (Mems[o_lp], Mems[n_lp], r_len)
+
+ LP_NREFS(o_lp) = op / I_SHIFT
+ LP_SETBLEN(o_lp, mod (op, I_SHIFT))
+ LP_SETBLEN(n_lp, r_len)
+ op = op + r_len
+ }
+ }
+
+ # Fix up the line index by accessing the tag word in the old line list
+ # in the old buffer to get the line list offset in the new buffer.
+
+ do i = 1, PL_NLP(pl) {
+ o_lp = Ref (pl, PL_LP(pl,i))
+ PL_LP(pl,i) = LP_NREF(o_lp) * I_SHIFT + LP_BLEN(o_lp)
+ }
+
+ # Deallocate the old buffer and install the new one.
+ call mfree (PL_LLBP(pl), TY_SHORT)
+
+ PL_LLBP(pl) = n_bp
+ PL_LLOP(pl) = op
+ PL_LLLEN(pl) = nwords
+ PL_LLFREE(pl) = 0
+ PL_LLNUPDATES(pl) = 0
+end
diff --git a/sys/plio/plcompare.x b/sys/plio/plcompare.x
new file mode 100644
index 00000000..59ba9d1f
--- /dev/null
+++ b/sys/plio/plcompare.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_COMPARE -- Compare two masks for equality, optionally noting any
+# differences on the output file. PL_EQUAL is returned if the two masks
+# are equivalent.
+
+int procedure pl_compare (pl_1, pl_2, outfd)
+
+pointer pl_1, pl_2 #I masks to be compared
+int outfd #I file for diagnostic output, or NULL
+
+int i
+bool pll_equal()
+
+begin
+ if (PL_NAXES(pl_1) != PL_NAXES(pl_2) || PL_NLP(pl_1) != PL_NLP(pl_2)) {
+ if (outfd != NULL)
+ call fprintf (outfd, "the masks are not the same size\n")
+ return (PL_NOTEQUAL)
+ }
+
+ do i = 1, PL_NLP(pl_1)
+ if (!pll_equal (LL(pl_1,PL_LP(pl_1,i)), LL(pl_2,PL_LP(pl_2,i)))) {
+ if (outfd != NULL) {
+ call fprintf (outfd, "masks differ at line %d\n")
+ call pargi (i)
+ }
+ return (PL_NOTEQUAL)
+ }
+
+ return (PL_EQUAL)
+end
diff --git a/sys/plio/plcreate.x b/sys/plio/plcreate.x
new file mode 100644
index 00000000..fe972bcb
--- /dev/null
+++ b/sys/plio/plcreate.x
@@ -0,0 +1,22 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PL_CREATE -- Create an empty mask with the given dimensionality and size.
+# A newly created mask has all line pointers pointing to the empty line,
+# which is stored as the first entry in the line list buffer.
+
+pointer procedure pl_create (naxes, axlen, depth)
+
+int naxes #I number of axes (dimensionality of mask)
+long axlen[ARB] #I length of each axis
+int depth #I mask depth, bits
+
+pointer pl
+pointer pl_open()
+errchk pl_open
+
+begin
+ pl = pl_open (NULL)
+ call pl_ssize (pl, naxes, axlen, depth)
+
+ return (pl)
+end
diff --git a/sys/plio/pldbgout.x b/sys/plio/pldbgout.x
new file mode 100644
index 00000000..26885f3e
--- /dev/null
+++ b/sys/plio/pldbgout.x
@@ -0,0 +1,47 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PL_DEBUGOUT -- Output formatter for PL package debug output. Output as
+# many text items as will fit on a line, inserting one space between each
+# text item. A call with BUF=EOS terminates the sequence with a newline.
+
+procedure pl_debugout (fd, buf, col, firstcol, maxcol)
+
+int fd #I output stream
+char buf[ARB] #I text to be output
+int col #I next column of output
+int firstcol #I first column to write to
+int maxcol #I last column to write to
+
+int nchars
+int strlen()
+
+begin
+ nchars = min (maxcol-firstcol+1, strlen(buf))
+
+ if (nchars == 0) {
+ # Terminate the sequence with a newline.
+ call fprintf (fd, "\n")
+ col = 1
+ return
+
+ } else if (col + nchars > maxcol) {
+ # Break line and output token.
+ call fprintf (fd, "\n")
+
+ for (col=1; col < firstcol; col=col+1)
+ call putci (fd, ' ')
+
+ } else {
+ # Append to the current line.
+ if (col <= firstcol) {
+ for (; col < firstcol; col=col+1)
+ call putci (fd, ' ')
+ } else {
+ call putci (fd, ' ')
+ col = col + 1
+ }
+ }
+
+ call putline (fd, buf)
+ col = col + nchars
+end
diff --git a/sys/plio/pldebug.x b/sys/plio/pldebug.x
new file mode 100644
index 00000000..67a719dc
--- /dev/null
+++ b/sys/plio/pldebug.x
@@ -0,0 +1,218 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_DEBUG -- Print debug information for a mask. The information to be
+# printed is selected by summing the following bitflags:
+#
+# PD_SUMMARY mask level summary information
+# PD_INDEX print the line list index
+# PD_LLOUT print the line lists, line list format
+# PD_RLOUT print the line lists, range list format
+#
+# The mask is not modified in any way. All output is on the given stream,
+# formatted to the page width given by the 'width' argument.
+
+procedure pl_debug (pl, fd, width, what)
+
+pointer pl #I mask descriptor
+int fd #I output file
+int width #I max width of formatted output, columns
+int what #I flags defining what to print
+
+pointer sp, buf, rng, rl, pp
+int line_1, line_2, nne, nv, v, lp, i
+int naxes, axlen, nlp, firstcol, maxcol, col, rlen
+errchk pl_valid, fprintf, pl_debugout, pll_prints, plr_printi
+bool pl_empty()
+int pl_l2ri()
+
+define index_ 91
+define lines_ 92
+define done_ 93
+define llout_ 94
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+ call salloc (rng, SZ_FNAME, TY_CHAR)
+ call salloc (rl, RL_MAXLEN(pl), TY_INT)
+
+ call pl_valid (pl)
+ naxes = PL_NAXES(pl)
+ axlen = PL_AXLEN(pl,1)
+ nlp = PL_NLP(pl)
+ maxcol = width - 1
+
+ # Output the summary information.
+ # ---------------------------------
+
+ if (and (what, PD_SUMMARY) == 0)
+ goto index_
+
+ # Line 1 of summary.
+ call fprintf (fd, "Mask %x naxes=%d [")
+ call pargi (pl)
+ call pargi (naxes)
+ do i = 1, naxes {
+ call fprintf (fd, "%d%c")
+ call pargi (PL_AXLEN(pl,i))
+ if (i == naxes)
+ call pargi (']')
+ else
+ call pargi (',')
+ }
+ call fprintf (fd, " maxval=%o")
+ call pargi (PL_MAXVAL(pl))
+ call fprintf (fd, " plane=[")
+ do i = 1, naxes {
+ call fprintf (fd, "%d%c")
+ call pargi (PL_AXLEN(pl,i))
+ if (i == naxes)
+ call pargi (']')
+ else
+ call pargi (',')
+ }
+ call fprintf (fd, "\n")
+
+ # Line 2 of summary.
+ call fprintf (fd,
+ "max buffered line size %d, max actual line size %d\n")
+ call pargi (PL_MAXLINE(pl))
+ v = 0
+ nne = 0
+ do i = 1, nlp {
+ lp = PL_LP(pl,i)
+ nv = LP_LEN (Ref (pl, lp))
+ if (nv > v)
+ v = nv
+ if (lp != PL_EMPTYLINE)
+ nne = nne + 1
+ }
+ call pargi (v)
+
+ # Line 3 of summary.
+ call fprintf (fd, "%d lines total, %d are nonempty, mask is %s\n")
+ call pargi (nlp)
+ call pargi (nne)
+ if (pl_empty (pl))
+ call pargstr ("empty")
+ else
+ call pargstr ("nonempty")
+
+ # Line 4 of summary.
+ call fprintf (fd, "llbp=%x, len=%d, op=%d, free=%d, nupdates=%d\n")
+ call pargi (PL_LLBP(pl))
+ call pargi (PL_LLLEN(pl))
+ call pargi (PL_LLOP(pl))
+ call pargi (PL_LLFREE(pl))
+ call pargi (PL_LLNUPDATES(pl))
+
+index_
+ # Print index.
+ # ---------------------------------
+
+ if (and (what, PD_INDEX) == 0)
+ goto lines_
+
+ call fprintf (fd, "Index at %x containing %d lines:\n")
+ call pargi (PL_LPP(pl))
+ call pargi (nlp)
+ col = 1
+ firstcol = 1
+ do i = 1, nlp {
+ lp = PL_LP(pl,i)
+ call sprintf (Memc[buf], SZ_LINE, "%6d")
+ call pargi (lp)
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ }
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+
+lines_
+ # Print the line list.
+ # ---------------------------------
+
+ if (and (what, PD_LLOUT+PD_RLOUT+PD_LHDR) == 0)
+ goto done_
+
+ call fprintf (fd, "Line list containing %d lines:\n")
+ call pargi (nlp)
+
+ line_1 = 0
+ do i = 1, nlp + 1 {
+ if (i > nlp && line_1 != 0)
+ goto llout_
+ lp = PL_LP(pl,i)
+
+ if (lp == PL_EMPTYLINE && line_1 == 0) {
+ # Skip over an empty line.
+ next
+ } else if (line_1 == 0) {
+ # Begin a new region.
+ line_1 = i
+ line_2 = i
+ if (i == nlp)
+ goto llout_
+ } else if (lp == PL_LP(pl,line_1)) {
+ # Add line to current region.
+ line_2 = i
+ if (i == nlp)
+ goto llout_
+
+ } else {
+ # Output a region.
+llout_
+ lp = PL_LP(pl,line_1)
+ pp = Ref (pl, lp)
+
+ if (line_1 == line_2) {
+ call sprintf (Memc[rng], SZ_FNAME, "[%d]")
+ call pargi (line_1)
+ } else {
+ call sprintf (Memc[rng], SZ_FNAME, "[%d:%d]")
+ call pargi (line_1)
+ call pargi (line_2)
+ }
+
+ if (and (what, PD_LHDR) != 0) {
+ call sprintf (Memc[buf], SZ_LINE,
+ "%s%12tlp=%5d, nref=%d, blen=%d, len=%d")
+ call pargstr (Memc[rng])
+ call pargi (lp)
+ call pargi (LP_NREF(pp))
+ call pargi (LP_BLEN(pp))
+ call pargi (LP_LEN(pp))
+ } else
+ call strcpy (Memc[rng], Memc[buf], SZ_LINE)
+
+ # Output the line list as a line list.
+ firstcol = 12
+ if (and (what, PD_LLOUT) != 0)
+ call pll_prints (LL(pl,lp), fd, Memc[buf], firstcol, maxcol)
+
+ # Output as a range list.
+ if (and (what, PD_RLOUT) != 0) {
+ rlen = pl_l2ri (LL(pl,lp), 1, Memi[rl], axlen)
+ call plr_printi (Memi[rl], fd, Memc[buf], firstcol, maxcol)
+ }
+
+ if (and (what, PD_RLOUT+PD_LLOUT) == 0) {
+ col = 1; firstcol = 1
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+ }
+
+ if (PL_LP(pl,i) == PL_EMPTYLINE || line_2 == i)
+ line_1 = 0
+ else {
+ line_1 = i
+ line_2 = i
+ }
+ }
+ }
+
+done_
+ call flush (fd)
+ call sfree (sp)
+end
diff --git a/sys/plio/plempty.x b/sys/plio/plempty.x
new file mode 100644
index 00000000..2dab29ed
--- /dev/null
+++ b/sys/plio/plempty.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_EMPTY -- Test whether a mask is empty, i.e., contains no nonzero pixels.
+
+bool procedure pl_empty (pl)
+
+pointer pl #I mask descriptor
+int i
+
+begin
+ # Mask is empty if all lines are empty.
+ do i = 1, PL_NLP(pl)
+ if (PL_LP(pl,i) != PL_EMPTYLINE)
+ return (false)
+
+ return (true)
+
+ # The following also works, but the call to pl_compress is an
+ # unintended side effect which it is best to avoid.
+
+ # call pl_compress (pl)
+ # return (PL_LLOP(pl) == LP_BLEN(Ref(pl,0)))
+end
diff --git a/sys/plio/plemptyline.x b/sys/plio/plemptyline.x
new file mode 100644
index 00000000..4d4612cd
--- /dev/null
+++ b/sys/plio/plemptyline.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+
+# PL_EMPTYLINE -- Return a pointer to the empty line for a mask.
+
+pointer procedure pl_emptyline (pl)
+
+pointer pl #I mask descriptor
+
+begin
+ return (Ref (pl, PL_EMPTYLINE))
+end
diff --git a/sys/plio/plglls.x b/sys/plio/plglls.x
new file mode 100644
index 00000000..aaae0e42
--- /dev/null
+++ b/sys/plio/plglls.x
@@ -0,0 +1,39 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GLLS -- Get a line segment as a list list, applying the given ROP to
+# combine the pixels with those of the output line list.
+
+procedure pl_glls (pl, v, ll_dst, ll_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short ll_dst[ARB] #O output line list
+int ll_depth #I line list depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_out, ll_src
+pointer pl_access()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1)) {
+ ll_len = LP_LEN(ll_src)
+ call amovs (Mems[ll_src], ll_dst, ll_len)
+
+ } else {
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+
+ call pl_linerop (Mems[ll_src], v[1], PL_MAXVAL(pl), ll_dst, 1,
+ MV(ll_depth), Mems[ll_out], npix, rop)
+ ll_len = LP_LEN(ll_out)
+ call amovs (Mems[ll_out], ll_dst, ll_len)
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/plglp.gx b/sys/plio/plglp.gx
new file mode 100644
index 00000000..d5803033
--- /dev/null
+++ b/sys/plio/plglp.gx
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GLP -- Get a line segment as a pixel array, applying the given ROP to
+# combine the pixels with those of the output array.
+
+procedure pl_glp$t (pl, v, px_dst, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+PIXEL px_dst[ARB] #O output pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int np
+pointer sp, px_out, ll_src
+pointer pl_access()
+int pl_l2p$t()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop)) {
+ np = pl_l2p$t (Mems[ll_src], v[1], px_dst, npix)
+ return
+ }
+
+ call smark (sp)
+ call salloc (px_out, npix, TY_PIXEL)
+
+ np = pl_l2p$t (Mems[ll_src], v[1], Mem$t[px_out], npix)
+ call pl_pixrop$t (Mem$t[px_out], 1, PL_MAXVAL(pl),
+ px_dst, 1, MV(px_depth), npix, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plglr.gx b/sys/plio/plglr.gx
new file mode 100644
index 00000000..3a63346d
--- /dev/null
+++ b/sys/plio/plglr.gx
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+include <plset.h>
+
+# PL_GLR -- Get a line segment as a range list, applying the given ROP to
+# combine the pixels with those of the output list.
+
+procedure pl_glr$t (pl, v, rl_dst, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+PIXEL rl_dst[ARB] #O output range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int mr, nr
+pointer sp, rl_out, rl_src, ll_src
+pointer pl_access()
+int pl_l2r$t()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop))
+ nr = pl_l2r$t (Mems[ll_src], v[1], rl_dst, npix)
+ else {
+ call smark (sp)
+ mr = min (RL_MAXLEN(pl), npix * 3)
+ call salloc (rl_src, mr, TY_PIXEL)
+ call salloc (rl_out, mr, TY_PIXEL)
+
+ nr = pl_l2r$t (Mems[ll_src], v[1], Mem$t[rl_src], npix)
+ call pl_rangerop$t (Mem$t[rl_src], 1, PL_MAXVAL(pl),
+ rl_dst, 1, MV(rl_depth),
+ Mem$t[rl_out], npix, rop)
+
+ # Copy out the edited range list.
+ call amov$t (Mem$t[rl_out], rl_dst, RL$T_LEN(rl_out))
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/plgplane.x b/sys/plio/plgplane.x
new file mode 100644
index 00000000..3f338178
--- /dev/null
+++ b/sys/plio/plgplane.x
@@ -0,0 +1,15 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GETPLANE -- Get the 2-Dim plane to be referenced in calls to the pl_box,
+# pl_circle, etc. geometric region masking operators.
+
+procedure pl_getplane (pl, v)
+
+pointer pl #I mask descriptor
+long v[ARB] #O vector defining plane
+
+begin
+ call amovl (PL_PLANE(pl,1), v, PL_MAXDIM)
+end
diff --git a/sys/plio/plgsize.x b/sys/plio/plgsize.x
new file mode 100644
index 00000000..40e73ac3
--- /dev/null
+++ b/sys/plio/plgsize.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_GSIZE -- Get the dimensionality and size of a mask.
+
+procedure pl_gsize (pl, naxes, axlen, depth)
+
+pointer pl #I mask descriptor
+int naxes #O number of axes (dimensionality of mask)
+long axlen[ARB] #O length of each axis
+int depth #O mask depth, bits
+
+int i
+
+begin
+ naxes = PL_NAXES(pl)
+ call amovl (PL_AXLEN(pl,1), axlen, PL_MAXDIM)
+
+ do i = 0, ARB
+ if (2**i > min (I_PVMAX, PL_MAXVAL(pl))) {
+ depth = i
+ break
+ }
+end
diff --git a/sys/plio/pll2p.gx b/sys/plio/pll2p.gx
new file mode 100644
index 00000000..007af7a0
--- /dev/null
+++ b/sys/plio/pll2p.gx
@@ -0,0 +1,105 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_L2P -- Convert a line list to a pixel array. The number of pixels output
+# (always npix) is returned as the function value.
+
+int procedure pl_l2p$t (ll_src, xs, px_dst, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+PIXEL px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+PIXEL pv
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, ip, op, otop, i
+define putpix_ 91
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ skipword = false
+ op = 1
+ x1 = 1
+ pv = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN, I_HN, I_PN:
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+
+ # Process segment if any region is inbounds.
+ np = i2 - i1 + 1
+ if (np > 0) {
+ otop = op + np - 1
+ if (opcode == I_HN) {
+ do i = op, otop
+ px_dst[i] = pv
+ } else {
+ do i = op, otop
+ px_dst[i] = 0
+ if (opcode == I_PN && i2 == x2)
+ px_dst[otop] = pv
+ }
+ op = otop + 1
+ }
+
+ # Advance the line index.
+ x1 = x2 + 1
+
+ case I_SH:
+ pv = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ case I_IH:
+ pv = pv + data
+ case I_DH:
+ pv = pv - data
+ case I_IS:
+ pv = pv + data
+ goto putpix_
+ case I_DS:
+ pv = pv - data
+putpix_
+ if (x1 >= xs && x1 <= xe) {
+ px_dst[op] = pv
+ op = op + 1
+ }
+ x1 = x1 + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/pll2r.gx b/sys/plio/pll2r.gx
new file mode 100644
index 00000000..ae6a6fcf
--- /dev/null
+++ b/sys/plio/pll2r.gx
@@ -0,0 +1,117 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_L2R -- Convert a line list to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_l2r$t (ll_src, xs, rl, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+PIXEL rl[3,ARB] #O output range list
+int npix #I number of pixels to convert
+
+int pv, hi
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, rn, ip
+define range_ 91
+define putrange_ 92
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ rn = RL_FIRST
+ xe = xs + npix - 1
+ skipword = false
+ x1 = 1
+ hi = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ pv = 0
+ goto range_
+ case I_HN:
+ pv = hi
+range_
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+ np = i2 - i1 + 1
+ x1 = x2 + 1
+
+ case I_PN:
+ pv = hi
+ x2 = x1 + data - 1
+ if (x2 < xs || x2 > xe)
+ np = 0
+ else {
+ i1 = x2
+ np = 1
+ }
+ x1 = x2 + 1
+
+ case I_SH:
+ hi = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ next
+ case I_IH:
+ hi = hi + data
+ next
+ case I_DH:
+ hi = hi - data
+ next
+
+ case I_IS, I_DS:
+ if (opcode == I_IS)
+ hi = hi + data
+ else
+ hi = hi - data
+
+ i1 = max (x1, xs)
+ i2 = min (x1, xe)
+ np = i2 - i1 + 1
+ x1 = x1 + 1
+ pv = hi
+ }
+
+ # Output a range entry?
+ if (np > 0 && pv > 0) {
+ rl[1,rn] = i1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/pllen.x b/sys/plio/pllen.x
new file mode 100644
index 00000000..a09b3770
--- /dev/null
+++ b/sys/plio/pllen.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+
+# PL_LLEN -- Return the length of an encoded line list.
+
+int procedure pl_llen (ll)
+
+short ll[ARB] #I encoded line list
+
+begin
+ return (LL_LEN(ll))
+end
diff --git a/sys/plio/plleq.x b/sys/plio/plleq.x
new file mode 100644
index 00000000..d74ae370
--- /dev/null
+++ b/sys/plio/plleq.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PLL_EQUAL -- Compare two line lists for equality.
+
+bool procedure pll_equal (l1, l2)
+
+short l1[ARB] #I line list 1
+short l2[ARB] #I line list 2
+
+int i, off
+int l1_len, l1_first
+int l2_len, l2_first
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(l1)) {
+ l1_len = OLL_LEN(l1)
+ l1_first = OLL_FIRST
+ } else {
+ l1_len = LL_LEN(l1)
+ l1_first = LL_FIRST(l1)
+ }
+
+ # Support old format line lists.
+ if (LL_OLDFORMAT(l2)) {
+ l2_len = OLL_LEN(l2)
+ l2_first = OLL_FIRST
+ } else {
+ l2_len = LL_LEN(l2)
+ l2_first = LL_FIRST(l2)
+ }
+
+ if (l1_len != l2_len)
+ return (false)
+
+ off = l2_first - l1_first
+ do i = l1_first, l1_len
+ if (l1[i] != l2[i+off])
+ return (false)
+
+ return (true)
+end
diff --git a/sys/plio/plline.x b/sys/plio/plline.x
new file mode 100644
index 00000000..54caf253
--- /dev/null
+++ b/sys/plio/plline.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <math.h>
+include <plset.h>
+include <plio.h>
+
+# PL_LINE -- Perform a rasterop operation upon a line of arbitrary width drawn
+# at an arbitrary orientation in a 2-dimensional plane of a mask. If the
+# dimensionality of the mask exceeds 2, the pl_setplane() procedure should be
+# called first to define the plane of the mask to be modified.
+
+procedure pl_line (pl, x1, y1, x2, y2, width, rop)
+
+pointer pl #I mask descriptor
+int x1,y1 #I start point of line
+int x2,y2 #I end point of line
+int width #I width of line to be drawn, pixels
+int rop #I rasterop defining operation
+
+int npts
+int x[4], y[4]
+real theta, hwidth, dx, dy
+
+begin
+ dx = x2 - x1
+ dy = y2 - y1
+
+ # Compute the line direction and halfwidth.
+ hwidth = max (1.0, real(width)) / 2.0 - 0.001
+ if (abs(dx) < 0.0001) {
+ if (dy > 0)
+ theta = HALFPI
+ else
+ theta = -HALFPI
+ } else if (abs(dy) < 0.0001) {
+ if (dx > 0)
+ theta = 0.0
+ else
+ theta = PI
+ } else
+ theta = atan2 (dy, dx)
+
+ # Construct a polyline to be filled to draw the line.
+ if (width < 1.0001) {
+ x[1] = x1; y[1] = y1
+ x[2] = x2; y[2] = y2
+ npts = 2
+
+ } else {
+ x[1] = x1 + nint (hwidth * cos(theta+HALFPI))
+ y[1] = y1 + nint (hwidth * sin(theta+HALFPI))
+
+ x[2] = x1 + nint (hwidth * cos(theta-HALFPI))
+ y[2] = y1 + nint (hwidth * sin(theta-HALFPI))
+
+ x[3] = x2 + nint (hwidth * cos(theta-HALFPI))
+ y[3] = y2 + nint (hwidth * sin(theta-HALFPI))
+
+ x[4] = x2 + nint (hwidth * cos(theta+HALFPI))
+ y[4] = y2 + nint (hwidth * sin(theta+HALFPI))
+ npts = 4
+ }
+
+ # Draw the line.
+ call pl_polygon (pl, x, y, npts, rop)
+end
diff --git a/sys/plio/pllinene.x b/sys/plio/pllinene.x
new file mode 100644
index 00000000..3babc889
--- /dev/null
+++ b/sys/plio/pllinene.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_LINENOTEMPTY -- Test whether the indicated mask image line is empty.
+
+bool procedure pl_linenotempty (pl, v)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I coordinates of desired line
+
+int pl_reference()
+
+begin
+ return (pl_reference(pl,v) != PL_EMPTYLINE)
+end
diff --git a/sys/plio/pllnext.x b/sys/plio/pllnext.x
new file mode 100644
index 00000000..43d87c01
--- /dev/null
+++ b/sys/plio/pllnext.x
@@ -0,0 +1,61 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+include "pllseg.h"
+
+# PLL_NEXTSEG -- Internal routine called by the PLLSEG.H routines to get the
+# next segment of a line list.
+
+procedure pll_nextseg (ll, ld)
+
+short ll[ARB] #I input line list
+int ld[LEN_PLLDES] #I list list i/o descriptor
+
+int ip
+int opcode, data
+
+begin
+ for (ip = ld_ip(ld); ip <= LL_LEN(ll); ip = ld_ip(ld)) {
+ ld_ip(ld) = ip + 1
+ opcode = I_OPCODE(ll[ip])
+ data = I_DATA(ll[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ ld_nleft(ld) = data
+ ld_value(ld) = 0
+ return
+ case I_HN:
+ ld_nleft(ld) = data
+ ld_value(ld) = ld_hi(ld)
+ return
+ case I_PN:
+ ld_nleft(ld) = data - 1
+ ld_value(ld) = 0
+ ld_next_nleft(ld) = 1
+ ld_next_value(ld) = ld_hi(ld)
+ return
+
+ case I_SH:
+ ip = ip + 1
+ ld_ip(ld) = ip + 1
+ ld_hi(ld) = (int(ll[ip]) * I_SHIFT) + data
+ case I_IH:
+ ld_hi(ld) = ld_hi(ld) + data
+ case I_DH:
+ ld_hi(ld) = ld_hi(ld) - data
+
+ case I_IS, I_DS:
+ if (opcode == I_IS)
+ ld_hi(ld) = ld_hi(ld) + data
+ else
+ ld_hi(ld) = ld_hi(ld) - data
+
+ ld_nleft(ld) = 1
+ ld_value(ld) = ld_hi(ld)
+ return
+ }
+ }
+
+ ld_value(ld) = 0
+end
diff --git a/sys/plio/plload.x b/sys/plio/plload.x
new file mode 100644
index 00000000..d67fc595
--- /dev/null
+++ b/sys/plio/plload.x
@@ -0,0 +1,83 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_LOAD -- Load a mask from a buffer encoded in a machine independent format
+# in a previous call to PL_SAVE. The given mask descriptor may be either
+# inactive or active. In the case of a load into an active mask, the existing
+# mask is clobbered, and the mask may change size.
+
+procedure pl_load (pl, bp)
+
+pointer pl #I mask descriptor
+pointer bp #I buffer pointer (to short)
+
+pointer sp, index, ex, ip
+int o_lllen, o_nlp, sz_index, flags, nlp, i
+errchk malloc, realloc, syserr
+int pl_l2pi()
+pointer coerce()
+
+begin
+ call smark (sp)
+ call salloc (ex, LEN_PLEXTERN, TY_STRUCT)
+
+ o_lllen = PL_LLLEN(pl)
+ o_nlp = PL_NLP(pl)
+
+ # Decode the external format header structure, a fixed size structure
+ # stored in 32 bit MII integer format.
+
+ call miiupk32 (Memi[coerce(bp,TY_SHORT,TY_INT)], Memi[ex],
+ LEN_PLEXTERN, TY_STRUCT)
+ if (PLE_MAGIC(ex) != PL_MAGICVAL)
+ call syserr (SYS_PLBADSAVEF)
+
+ call amovi (PLE_AXLEN(ex,1), PL_AXLEN(pl,1), PL_MAXDIM)
+ PL_MAGIC(pl) = PLE_MAGIC(ex)
+ PL_NAXES(pl) = PLE_NAXES(ex)
+ PL_LLOP(pl) = PLE_LLOP(ex)
+ PL_LLLEN(pl) = PLE_LLLEN(ex)
+ PL_MAXLINE(pl) = PLE_MAXLINE(ex)
+ PL_MAXVAL(pl) = PLE_MAXVAL(ex)
+ PL_NLP(pl) = PLE_NLP(ex)
+ sz_index = PLE_NLPX(ex)
+ flags = PLE_FLAGS(ex)
+
+ # Get the (compressed) line index. If the descriptor is already active
+ # the new mask may be a different size than the old one.
+
+ nlp = 1
+ do i = 2, PL_NAXES(pl)
+ nlp = nlp * PL_AXLEN(pl,i)
+ if (PL_LPP(pl) == NULL)
+ call malloc (PL_LPP(pl), nlp, TY_INT)
+ else if (nlp != o_nlp)
+ call realloc (PL_LPP(pl), nlp, TY_INT)
+
+ call salloc (index, sz_index, TY_SHORT)
+ #ip = bp + (LEN_PLEXTERN * SZ_STRUCT) / SZ_SHORT
+ ip = bp + (LEN_PLEXTERN * SZ_MII_INT) / SZ_SHORT
+ call miiupk16 (Mems[ip], Mems[index], sz_index, TY_SHORT)
+ PL_NLP(pl) = pl_l2pi (Mems[index], 1, PL_LP(pl,1), nlp)
+
+ # Allocate or resize the line list buffer.
+ if (PL_LLBP(pl) == NULL)
+ call malloc (PL_LLBP(pl), PL_LLLEN(pl), TY_SHORT)
+ else if (PL_LLLEN(pl) != o_lllen)
+ call realloc (PL_LLBP(pl), PL_LLLEN(pl), TY_SHORT)
+
+ # Read the stored line list.
+ ip = ip + sz_index
+ call miiupk16 (Mems[ip], LL(pl,0), PL_LLLEN(pl), TY_SHORT)
+
+ # Update the remaining fields of the mask descriptor.
+ PL_LLFREE(pl) = 0
+ PL_LLNUPDATES(pl) = 0
+ PL_LLINC(pl) = PL_STARTINC
+ call amovki (1, PL_PLANE(pl,1), PL_MAXDIM)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plloadf.x b/sys/plio/plloadf.x
new file mode 100644
index 00000000..978086b8
--- /dev/null
+++ b/sys/plio/plloadf.x
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_LOADF -- Load a mask stored in external format in a binary file. This
+# simple code permits only one mask per file; more sophisticated storage
+# facilities are planned; these will probably obsolete this routine.
+
+procedure pl_loadf (pl, mask, title, maxch)
+
+pointer pl #I mask descriptor
+char mask[ARB] #I mask file
+char title[maxch] #O mask title
+int maxch #I max chars out
+
+int fd, nchars
+pointer sp, bp, sv, text, fname, extn
+int open(), read(), miireadc(), miireadi(), fnextn()
+errchk open, read, syserrs
+define err_ 91
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+ call salloc (extn, SZ_FNAME, TY_CHAR)
+
+ # Get mask file name.
+ call strcpy (mask, Memc[fname], SZ_PATHNAME)
+ if (fnextn (mask, Memc[extn], SZ_FNAME) <= 0)
+ call strcat (".pl", Memc[fname], SZ_PATHNAME)
+
+ # Open the mask save file.
+ fd = open (Memc[fname], READ_ONLY, BINARY_FILE)
+
+ # Get savefile header.
+ call salloc (sv, LEN_SVDES, TY_STRUCT)
+ if (miireadi (fd, Memi[sv], LEN_SVDES) != LEN_SVDES)
+ goto err_
+
+ # Verify file type.
+ if (SV_MAGIC(sv) != PLIO_SVMAGIC)
+ goto err_
+
+ # Get descriptive text.
+ call salloc (text, SV_TITLELEN(sv), TY_CHAR)
+ if (miireadc (fd, Memc[text], SV_TITLELEN(sv)) != SV_TITLELEN(sv))
+ goto err_
+ else
+ call strcpy (Memc[text], title, maxch)
+
+ # Get encoded mask.
+ call salloc (bp, SV_MASKLEN(sv), TY_SHORT)
+ iferr (nchars = read (fd, Mems[bp], SV_MASKLEN(sv) * SZ_SHORT))
+ goto err_
+ call close (fd)
+
+ iferr (call pl_load (pl, bp))
+ goto err_
+
+ call sfree (sp)
+ return
+err_
+ call close (fd)
+ call syserrs (SYS_PLBADSAVEF, Memc[fname])
+end
diff --git a/sys/plio/plloadim.x b/sys/plio/plloadim.x
new file mode 100644
index 00000000..6fc45adc
--- /dev/null
+++ b/sys/plio/plloadim.x
@@ -0,0 +1,99 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <plset.h>
+include <plio.h>
+
+# PL_LOADIM -- Load a mask stored as a conventional image, i.e., convert an
+# image to a mask.
+
+procedure pl_loadim (pl, imname, title, maxch)
+
+pointer pl #I mask descriptor
+char imname[ARB] #I image name or section
+char title[ARB] #O mask title
+int maxch #I max chars out
+
+bool sampling
+pointer im, px, im_pl, bp
+long vs_l[PL_MAXDIM], vs_p[PL_MAXDIM]
+long ve_l[PL_MAXDIM], ve_p[PL_MAXDIM]
+int npix, naxes, maxdim, maxval, depth, sz_buf, i
+long v_in[PL_MAXDIM], v_out[PL_MAXDIM], vn[PL_MAXDIM]
+
+pointer immap()
+int imgnli(), imstati(), im_pmsvhdr()
+errchk immap, imgnli
+
+begin
+ # Open the input image.
+ im = immap (imname, READ_ONLY, 0)
+
+ # Encode and output the image header.
+ bp = NULL; i = im_pmsvhdr (im, bp, sz_buf)
+ call strcpy (Memc[bp], title, maxch)
+ call mfree (bp, TY_CHAR)
+
+ # Determine the mask depth in bits.
+ maxval = IM_MAX(im)
+ if (maxval <= 0)
+ maxval = I_PVMAX
+ do i = 0, ARB
+ if (2**i > min (I_PVMAX, maxval)) {
+ depth = i
+ break
+ }
+
+ # Initialize the mask to the size of the image section.
+ npix = IM_LEN(im,1)
+ naxes = IM_NDIM(im)
+ maxdim = min (IM_MAXDIM, PL_MAXDIM)
+ call amovl (IM_LEN(im,1), vn, maxdim)
+ call pl_ssize (pl, naxes, vn, depth)
+
+ # If the image is already a mask internally, check whether any
+ # subsampling, axis flipping, or axis mapping is in effect.
+ # If so we can't use PLIO to copy the mask section.
+
+ im_pl = imstati (im, IM_PLDES)
+ sampling = false
+
+ if (im_pl != NULL) {
+ call amovkl (long(1), vs_l, maxdim)
+ call amovl (IM_LEN(im,1), ve_l, maxdim)
+ call imaplv (im, vs_l, vs_p, maxdim)
+ call imaplv (im, ve_l, ve_p, maxdim)
+
+ do i = 1, maxdim {
+ vn[i] = ve_l[i] - vs_l[i] + 1
+ if (vn[i] != ve_p[i] - vs_p[i] + 1) {
+ sampling = true
+ break
+ }
+ }
+ }
+
+ # If the source image is already a mask internally and no axis
+ # geometry is in effect in the image section (if any), then we can
+ # use a PLIO rasterop to efficiently copy the mask subsection.
+
+ if (im_pl != NULL && !sampling) {
+ # Copy a mask subsection (or entire image if no section).
+ call pl_rop (im_pl, vs_p, pl, vs_l, vn, PIX_SRC)
+
+ } else {
+ # Copy image pixels. Initialize the vector loop indices.
+ call amovkl (long(1), v_in, maxdim)
+ call amovkl (long(1), v_out, maxdim)
+
+ # Copy the image.
+ while (imgnli (im, px, v_in) != EOF) {
+ call pl_plpi (pl, v_out, Memi[px], 0, npix, PIX_SRC)
+ call amovl (v_in, v_out, maxdim)
+ }
+ }
+
+ call pl_compress (pl)
+ call imunmap (im)
+end
diff --git a/sys/plio/plloop.x b/sys/plio/plloop.x
new file mode 100644
index 00000000..500dc8bb
--- /dev/null
+++ b/sys/plio/plloop.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PLLOOP -- Increment the vector V from VS to VE (nested do loops cannot
+# be used because of the variable number of dimensions). Return LOOP_DONE
+# when V exceeds VE.
+
+int procedure plloop (v, vs, ve, ndim)
+
+long v[ndim] #U vector giving current position in image
+long vs[ndim] #I start vector
+long ve[ndim] #I end vector
+int ndim #I vector length
+
+int i
+
+begin
+ do i = 2, ndim {
+ v[i] = v[i] + 1
+ if (v[i] > ve[i]) {
+ if (i < ndim)
+ v[i] = vs[i]
+ else
+ break
+ } else
+ return (LOOP_AGAIN)
+ }
+
+ return (LOOP_DONE)
+end
diff --git a/sys/plio/pllpr.x b/sys/plio/pllpr.x
new file mode 100644
index 00000000..9a8d1684
--- /dev/null
+++ b/sys/plio/pllpr.x
@@ -0,0 +1,111 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PLL_PRINTS -- Print a line list on the given output stream.
+
+procedure pll_prints (ll, fd, label, firstcol, maxcols)
+
+short ll[ARB] #I line list
+int fd #I output file
+char label[ARB] #I line label
+int firstcol #I first column for output
+int maxcols #I width of formatted output
+
+pointer sp, buf
+bool skipword
+int opcode, data
+int ll_len, ll_first, col, ip, pv, x
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll)) {
+ ll_len = OLL_LEN(ll)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll)
+ ll_first = LL_FIRST(ll)
+ }
+
+ # Output the line label and advance to the first column. If the label
+ # extends beyond the first column, start a new line.
+
+ call putline (fd, label)
+ col = strlen (label) + 1
+ if (col > firstcol)
+ call pl_debugout (fd, "", col, firstcol, maxcols)
+
+ skipword = false
+ pv = 1
+ x = 1
+
+ # Decode the line list proper.
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll[ip])
+ data = I_DATA(ll[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ x = x + data
+ call sprintf (Memc[buf], SZ_FNAME, "Z%d")
+ call pargi (data)
+ case I_HN:
+ x = x + data
+ call sprintf (Memc[buf], SZ_FNAME, "H%d")
+ call pargi (data)
+ case I_PN:
+ x = x + data
+ call sprintf (Memc[buf], SZ_FNAME, "P%d")
+ call pargi (data)
+
+ case I_SH:
+ pv = (int(ll[ip+1]) * I_SHIFT) + data
+ skipword = true
+ call sprintf (Memc[buf], SZ_FNAME, "SH(%d)")
+ call pargi (pv)
+ case I_IH:
+ pv = pv + data
+ call sprintf (Memc[buf], SZ_FNAME, "IH%d(%d)")
+ call pargi (data)
+ call pargi (pv)
+ case I_DH:
+ pv = pv - data
+ call sprintf (Memc[buf], SZ_FNAME, "DH%d(%d)")
+ call pargi (data)
+ call pargi (pv)
+
+ case I_IS, I_DS:
+ x = x + 1
+ if (opcode == I_IS) {
+ pv = pv + data
+ call sprintf (Memc[buf], SZ_FNAME, "IS%d(%d)")
+ call pargi (data)
+ call pargi (pv)
+ } else {
+ pv = pv - data
+ call sprintf (Memc[buf], SZ_FNAME, "DS%d(%d)")
+ call pargi (data)
+ call pargi (pv)
+ }
+ }
+
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcols)
+ }
+
+ call sprintf (Memc[buf], SZ_FNAME, "(%d,%d)")
+ call pargi (x - 1)
+ call pargi (pv)
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcols)
+
+ call pl_debugout (fd, "", col, firstcol, maxcols)
+ call sfree (sp)
+end
diff --git a/sys/plio/pllrop.x b/sys/plio/pllrop.x
new file mode 100644
index 00000000..2f5f1358
--- /dev/null
+++ b/sys/plio/pllrop.x
@@ -0,0 +1,271 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include "pllseg.h"
+include <plio.h>
+
+# PL_LINEROP -- Rasterop operation between source and destination line lists.
+# The indicated rasterop operation is performed upon the source and destination
+# line lists, writing the result to LL_OUT, which is a copy of LL_DST except
+# for the region affected by the rasterop operation (note that the destination
+# line list cannot be edited in place since it may change size).
+
+procedure pl_linerop (ll_src, xs, src_maxval,
+ ll_dst, ds, dst_maxval, ll_out, npix, rop)
+
+short ll_src[ARB] #I source line list
+int xs #I starting pixel index in src line list
+int src_maxval #I maximum pixel value, source mask
+short ll_dst[ARB] #I destination line list
+int ds #I starting pixel index in dst line list
+int dst_maxval #I maximum pixel value, dst mask
+short ll_out[ARB] #O output list (edited version of ll_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+int segsize, v_src, v_dst, pv
+bool need_src, need_dst, rop_enable
+int o_op, o_iz, o_pv, o_np, o_hi, src_value
+int opcode, data, nz, iz, x1, hi, dv, v, np, op, n, i
+int d_src[LEN_PLLDES], d_dst[LEN_PLLDES]
+define done_ 91
+
+begin
+ need_src = R_NEED_SRC(rop)
+ need_dst = R_NEED_DST(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Advance to the desired position in the source list, discarding
+ # the instructions once read. The point XS may lie within the range
+ # of an instruction.
+
+ if (need_src) {
+ x1 = 1
+ pll_init (ll_src, d_src)
+ do i = 1, ARB {
+ np = min (pll_nleft(d_src), xs - x1)
+ pll_getseg (ll_src, d_src, np, v_src)
+ x1 = x1 + np
+ if (x1 >= xs || np == 0)
+ break
+ }
+ }
+
+ # Copy DST to OUT, applying the rasterop in the region of NPIX pixels
+ # beginning at DS. To simplify things (avoid pathological complexity
+ # in this case) we suffer some unnecessary unpacking and repacking
+ # of encoded line list instructions in the regions of the DST list
+ # which are simply copied. This avoids the need for special treatment
+ # at the edges of the region to which the ROP applies. ROP_ENABLE is
+ # false initially, true in the ROP region, and false again to the
+ # right. The number of pixels in each region is given by SEGSIZE.
+
+ o_pv = -1
+ op = LL_CURHDRLEN + 1
+ segsize = ds - 1
+ rop_enable = false
+ x1 = 1; iz = 1; hi = 1
+ pll_init (ll_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been properly performed
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, pll_nleft(d_dst))
+ if (need_src && rop_enable && pll_nleft(d_src) > 0)
+ np = min (np, pll_nleft(d_src))
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We always
+ # have to read the DST list in order to copy the unmodified regions
+ # to the output. We read the SRC list and apply the rasterop only
+ # in the region to which the ROP applies.
+
+ pll_getseg (ll_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ # Get v_src.
+ if (need_src) {
+ v_src = 0
+ if (pll_nleft (d_src) > 0)
+ pll_getseg (ll_src, d_src, np, v_src)
+
+ if (R_NOTSRC(rop)) {
+ v_src = not (v_src)
+ if (src_maxval != 0)
+ v_src = and (v_src, src_maxval)
+ }
+
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ }
+
+ # Get v_dst.
+ if (need_dst) {
+ if (R_NOTDST(rop)) {
+ v_dst = not (v_dst)
+ if (dst_maxval != 0)
+ v_dst = and (v_dst, dst_maxval)
+ }
+ }
+
+ # Apply the rasterop.
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC, PIX_NOTSRC:
+ pv = v_src
+ case PIX_DST, PIX_NOTDST:
+ pv = v_dst
+ case PIX_SRC_AND_DST, PIX_SRC_AND_NOTDST, PIX_NOTSRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST, PIX_SRC_OR_NOTDST, PIX_NOTSRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, or map int
+ # to bool for the case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (dst_maxval, pv)
+
+ } else
+ pv = v_dst
+
+
+ if (pv == 0) {
+ if (pll_nleft (d_dst) <= 0) {
+ # Output zeros at end of list.
+ x1 = x1 + np
+ } else {
+ # Keep going until we get a nonzero range.
+ o_pv = 0
+ x1 = x1 + np
+ segsize = segsize - np
+ next
+ }
+ } else if (pv == o_pv) {
+ # Combine with previous range.
+ iz = o_iz
+ hi = o_hi
+ op = o_op
+ x1 = x1 - o_np
+ segsize = segsize + o_np
+ np = np + o_np
+ o_np = np
+ } else {
+ # Save current range parameters.
+ o_op = op
+ o_np = np
+ o_iz = iz
+ o_hi = hi
+ o_pv = pv
+ }
+
+ # Encode an instruction to regenerate the current range of NP data
+ # values of nonzero level PV, starting at X1. In the most complex
+ # case we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_out[op] = M_SH + and (pv, I_DATAMAX)
+ op = op + 1
+ ll_out[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_out[op] = M_DH + (-dv)
+ else
+ ll_out[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_out[op-1]
+ ll_out[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (n=nz; n > 0; n = n - I_DATAMAX) {
+ ll_out[op] = M_ZN + min(I_DATAMAX,n)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_out[op-1] = ll_out[op-1] + M_PN + 1
+ goto done_
+ }
+ # At end of list.
+ if (pv == 0)
+ goto done_
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (n=np; n > 0; n = n - I_DATAMAX) {
+ ll_out[op] = M_HN + min(I_DATAMAX,n)
+ op = op + 1
+ }
+done_
+ segsize = segsize - np
+ x1 = x1 + np
+ iz = x1
+ }
+
+ # Update the line list header.
+ call amovs (ll_dst, ll_out, LL_CURHDRLEN)
+ LL_SETLEN(ll_out, op - 1)
+end
diff --git a/sys/plio/pllseg.h b/sys/plio/pllseg.h
new file mode 100644
index 00000000..2f358944
--- /dev/null
+++ b/sys/plio/pllseg.h
@@ -0,0 +1,56 @@
+# PLLSEG.H -- Macros for sequentially reading segments of a line list.
+#
+# pll_init (ll, descriptor)
+# npix = pll_nleft (descriptor)
+# val = pll_getseg (ll, descriptor, npix, value)
+#
+# pll_init Initialize descriptor for sequential i/o from the linelist LL.
+# pll_nleft Number of pixels left in the current line segment of constant
+# value. Zero is returned at the EOL.
+# pll_getseg Read NPIX pixels from the current segment, advancing to the
+# next segment automatically when the the current segment is
+# exhausted.
+#
+# The descriptor is an integer array, the contents of which are hidden from
+# the application using these macros. This package uses the internal
+# procedure PLL_NEXTSEG, which is included in PL package library.
+
+# Range list i/o descriptor.
+define LEN_PLLDES 7
+define ld_nleft $1[1]
+define ld_value $1[2]
+define ld_x $1[3]
+define ld_ip $1[4]
+define ld_hi $1[5]
+define ld_next_nleft $1[6]
+define ld_next_value $1[7]
+
+# PLL_INIT -- Initialize the linelist descriptor.
+define (pll_init, { # $1=ll $2=des
+ # ld_x($2) = 1
+ ld_hi($2) = 1
+ if (LL_OLDFORMAT($1))
+ ld_ip($2) = OLL_FIRST
+ else
+ ld_ip($2) = LL_FIRST($1)
+ ld_next_nleft($2) = 0
+ ld_nleft($2) = 0
+ call pll_nextseg ($1, $2)
+})
+
+# PLL_NLEFT -- Number of pixels left in the current segment.
+define pll_nleft ld_nleft($1)
+
+# PLL_GETSEG -- Read pixels from the current segment.
+define (pll_getseg, { # $1=ll $2=des $3=npix $4=value
+ $4 = ld_value($2)
+ # ld_x($2) = ld_x($2) + $3
+ ld_nleft($2) = ld_nleft($2) - $3
+ if (ld_nleft($2) <= 0)
+ if (ld_next_nleft($2) > 0) {
+ ld_nleft($2) = ld_next_nleft($2)
+ ld_value($2) = ld_next_value($2)
+ ld_next_nleft($2) = 0
+ } else
+ call pll_nextseg ($1, $2)
+})
diff --git a/sys/plio/pllsten.x b/sys/plio/pllsten.x
new file mode 100644
index 00000000..cfd125f3
--- /dev/null
+++ b/sys/plio/pllsten.x
@@ -0,0 +1,289 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include "pllseg.h"
+include <plio.h>
+
+# PL_LINESTENCIL -- Rasterop operation between source and destination line
+# lists, reading the source and applying the rasterop only in the regions
+# specified by the stencil line list. The stencil list is aligned with the
+# source list.
+
+procedure pl_linestencil (ll_src,xs,src_maxval, ll_dst,ds,dst_maxval,
+ ll_stn,ss, ll_out, npix, rop)
+
+short ll_src[ARB] #I source line list
+int xs #I starting pixel index in src line list
+int src_maxval #I maximum pixel value, source mask
+short ll_dst[ARB] #I destination line list
+int ds #I starting pixel index in dst line list
+int dst_maxval #I maximum pixel value, destination mask
+short ll_stn[ARB] #I stencil line list
+int ss #I starting pixel index in stn line list
+short ll_out[ARB] #O output list (edited version of ll_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+bool need_src, rop_enable
+int o_op, o_iz, o_pv, o_np, o_hi
+int segsize, v_src, v_dst, v_stn, pv, src_value
+int opcode, data, x1, hi, dv, v, iz, nz, np, op, n, i
+int d_src[LEN_PLLDES], d_dst[LEN_PLLDES], d_stn[LEN_PLLDES]
+define copyout_ 91
+define done_ 92
+
+begin
+ need_src = R_NEED_SRC(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1)
+ src_value = data
+ if (src_value <= 0)
+ src_value = src_maxval
+
+ # Advance to the indicated position in the source list, discarding
+ # the instructions once read. The point XS may lie within the range
+ # of an instruction.
+
+ if (need_src) {
+ x1 = 1
+ pll_init (ll_src, d_src)
+ do i = 1, ARB {
+ np = min (pll_nleft(d_src), xs - x1)
+ pll_getseg (ll_src, d_src, np, v_src)
+ x1 = x1 + np
+ if (x1 >= xs || np == 0)
+ break
+ }
+ }
+
+ # Advance to the indicated position in the stencil list. This causes
+ # the point SS in the stencil to be aligned with the point XS in the
+ # source.
+
+ if (need_src) {
+ x1 = 1
+ pll_init (ll_stn, d_stn)
+ do i = 1, ARB {
+ np = min (pll_nleft(d_stn), xs - x1)
+ pll_getseg (ll_stn, d_stn, np, v_stn)
+ x1 = x1 + np
+ if (x1 >= xs || np == 0)
+ break
+ }
+ }
+
+ # Copy DST to OUT, applying the rasterop in the region of NPIX pixels
+ # beginning at DS. To simplify things (avoid pathological complexity
+ # in this case) we suffer some unnecessary unpacking and repacking
+ # of encoded line list instructions in the regions of the DST list
+ # which are simply copied. This avoids the need for special treatment
+ # at the edges of the region to which the ROP applies. ROP_ENABLE is
+ # false initially, true in the ROP region, and false again to the
+ # right. The number of pixels in each region is given by SEGSIZE.
+
+ o_pv = -1
+ op = LL_CURHDRLEN + 1
+ segsize = ds - 1
+ rop_enable = false
+ x1 = 1; iz = 1; hi = 1
+ pll_init (ll_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been performed properly
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, pll_nleft(d_dst))
+ if (need_src && rop_enable && pll_nleft(d_src) > 0) {
+ np = min (np, pll_nleft(d_src))
+ if (pll_nleft (d_stn) > 0)
+ np = min (np, pll_nleft(d_stn))
+ }
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We always
+ # have to read the DST list to copy it to the output. We read the
+ # SRC list and apply the rasterop only in the region to which the
+ # ROP applies.
+
+ pll_getseg (ll_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ if (need_src) {
+ v_src = 0
+ if (pll_nleft (d_src) > 0)
+ pll_getseg (ll_src, d_src, np, v_src)
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ pll_getseg (ll_stn, d_stn, np, v_stn)
+ if (v_stn == 0)
+ goto copyout_
+ }
+
+ # Compute the pixel value for the new output segment, given the
+ # source and destination pixel values and the rasterop supplied.
+
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC:
+ pv = v_src
+ case PIX_DST:
+ pv = v_dst
+ case PIX_NOTSRC:
+ pv = not (v_src)
+ case PIX_NOTDST:
+ pv = not (v_dst)
+ case PIX_SRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_SRC_AND_NOTDST:
+ pv = and (v_src, not(v_dst))
+ case PIX_SRC_OR_NOTDST:
+ pv = or (v_src, not(v_dst))
+ case PIX_NOTSRC_AND_DST:
+ pv = and (not(v_src), v_dst)
+ case PIX_NOTSRC_OR_DST:
+ pv = or (not(v_src), v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, handle the
+ # case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (dst_maxval, pv)
+
+ } else
+copyout_ pv = v_dst
+
+ if (pv == 0) {
+ if (pll_nleft (d_dst) <= 0) {
+ # Output zeros at end of list.
+ x1 = x1 + np
+ } else {
+ # Keep going until we get a nonzero range.
+ o_pv = 0
+ x1 = x1 + np
+ segsize = segsize - np
+ next
+ }
+ } else if (pv == o_pv) {
+ # Combine with previous range.
+ iz = o_iz
+ hi = o_hi
+ op = o_op
+ x1 = x1 - o_np
+ segsize = segsize + o_np
+ np = np + o_np
+ o_np = np
+ } else {
+ # Save current range parameters.
+ o_op = op
+ o_np = np
+ o_iz = iz
+ o_hi = hi
+ o_pv = pv
+ }
+
+ # Encode an instruction to regenerate the current range of NP data
+ # values of nonzero level PV, starting at X1. In the most complex
+ # case we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_out[op] = M_SH + and (pv, I_DATAMAX)
+ op = op + 1
+ ll_out[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_out[op] = M_DH + (-dv)
+ else
+ ll_out[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_out[op-1]
+ ll_out[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (n=nz; n > 0; n = n - I_DATAMAX) {
+ ll_out[op] = M_ZN + min(I_DATAMAX,n)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_out[op-1] = ll_out[op-1] + M_PN + 1
+ goto done_
+ }
+ # At end of list.
+ if (pv == 0)
+ goto done_
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (n=np; n > 0; n = n - I_DATAMAX) {
+ ll_out[op] = M_HN + min(I_DATAMAX,n)
+ op = op + 1
+ }
+done_
+ segsize = segsize - np
+ x1 = x1 + np
+ iz = x1
+ }
+
+ # Update the line list header.
+ call amovs (ll_dst, ll_out, LL_CURHDRLEN)
+ LL_SETLEN(ll_out, op - 1)
+end
diff --git a/sys/plio/plnewcopy.x b/sys/plio/plnewcopy.x
new file mode 100644
index 00000000..a429bc6e
--- /dev/null
+++ b/sys/plio/plnewcopy.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_NEWCOPY -- Create a new, empty mask with the same size and depth
+# attributes as the reference mask.
+
+pointer procedure pl_newcopy (old_pl)
+
+pointer old_pl #I mask descriptor
+
+pointer new_pl
+int naxes, depth
+long axlen[PL_MAXDIM]
+pointer pl_open()
+errchk pl_open
+
+begin
+ new_pl = pl_open (NULL)
+
+ call pl_gsize (old_pl, naxes, axlen, depth)
+ call pl_ssize (new_pl, naxes, axlen, depth)
+
+ PL_PRIVATE1(new_pl) = PL_PRIVATE1(old_pl)
+ PL_PRIVATE2(new_pl) = PL_PRIVATE2(old_pl)
+ PL_MAXLINE(new_pl) = PL_MAXLINE(old_pl)
+
+ return (new_pl)
+end
diff --git a/sys/plio/plopen.x b/sys/plio/plopen.x
new file mode 100644
index 00000000..6510ebe5
--- /dev/null
+++ b/sys/plio/plopen.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_OPEN -- Open a mask. If the input buffer pointer is NULL an inactive
+# mask descriptor is allocated, otherwise the pointer is taken to point to
+# an encoded mask, which is decoded and loaded to create an active descriptor.
+
+pointer procedure pl_open (smp)
+
+pointer smp #I stored mask pointer or NULL
+
+pointer pl
+errchk calloc, pl_load
+
+begin
+ # Allocate and initialize an inactive descriptor.
+ call calloc (pl, LEN_PLDES, TY_STRUCT)
+
+ call amovki (1, PL_PLANE(pl,1), PL_MAXDIM)
+ PL_MAGIC(pl) = PL_MAGICVAL
+ PL_LLINC(pl) = PL_STARTINC
+
+ # Load the saved mask, if any.
+ if (smp != NULL)
+ call pl_load (pl, smp)
+
+ return (pl)
+end
diff --git a/sys/plio/plp2l.gx b/sys/plio/plp2l.gx
new file mode 100644
index 00000000..85a59507
--- /dev/null
+++ b/sys/plio/plp2l.gx
@@ -0,0 +1,126 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_P2L -- Convert a pixel array to a line list. The length of the list is
+# returned as the function value.
+
+int procedure pl_p2l$t (px_src, xs, ll_dst, npix)
+
+PIXEL px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+PIXEL hi, pv, nv, zero
+int xe, x1, iz, ip, op, np, nz, dv, v
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+
+ # Pack the pixel array into a line list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ iz = xs # start index of range of zeros
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ } else if (pv == 0)
+ x1 = xe + 1
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ np = ip - x1 + 1
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ # The I_DATAMAX-1 limit is to allow adding M_PN+1 without
+ # overflowing the range of the data segment.
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ x1 = ip + 1
+ iz = x1
+ pv = nv
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/plp2r.gx b/sys/plio/plp2r.gx
new file mode 100644
index 00000000..2e1d1984
--- /dev/null
+++ b/sys/plio/plp2r.gx
@@ -0,0 +1,71 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_P2R -- Convert a pixel array to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_p2r$t (px_src, xs, rl, npix)
+
+PIXEL px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+PIXEL rl[3,ARB] #O destination range list
+int npix #I number of pixels to convert
+
+PIXEL hi, pv, zero
+int xe, x1, np, rn, nv, ip
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ rn = RL_FIRST
+
+ # Pack the pixel array into a range list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ }
+
+ np = ip - x1 + 1
+
+ # Output the new range.
+ if (pv > 0) {
+ rl[1,rn] = x1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ x1 = ip + 1
+ pv = nv
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/plplls.x b/sys/plio/plplls.x
new file mode 100644
index 00000000..8f8ca491
--- /dev/null
+++ b/sys/plio/plplls.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLLS -- Put a line segment input as a line list to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plls (pl, v, ll_src, ll_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short ll_src[ARB] #I input line list
+int ll_depth #I line list depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+pointer sp, ll_out, ll_dst
+pointer pl_access()
+errchk pl_access
+
+begin
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, ll_src)
+ else {
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (ll_src, 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(ll_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/plplp.gx b/sys/plio/plplp.gx
new file mode 100644
index 00000000..01a3596c
--- /dev/null
+++ b/sys/plio/plplp.gx
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLP -- Put a line segment input as a pixel array to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plp$t (pl, v, px_src, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+PIXEL px_src[ARB] #I input pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_p2l$t()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the pixel array to a line list.
+ ll_len = pl_p2l$t (px_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(px_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plplr.gx b/sys/plio/plplr.gx
new file mode 100644
index 00000000..d33ba40c
--- /dev/null
+++ b/sys/plio/plplr.gx
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLR -- Put a line segment input as a range list to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plr$t (pl, v, rl_src, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+PIXEL rl_src[ARB] #I input range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_r2l$t()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the range list to a line list.
+ ll_len = pl_r2l$t (rl_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(rl_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plpoint.x b/sys/plio/plpoint.x
new file mode 100644
index 00000000..de9b7ede
--- /dev/null
+++ b/sys/plio/plpoint.x
@@ -0,0 +1,62 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_POINT -- Perform a rasterop operation on a single point in a line of a
+# 2-dimensional plane of a mask. If the dimensionality of the mask exceeds 2,
+# the pl_setplane() procedure should be called first to define the plane of
+# the mask to be modified.
+
+procedure pl_point (pl, x, y, rop)
+
+pointer pl #I mask descriptor
+int x #I pixel to be modified
+int y #I line to be modified
+int rop #I rasterop defining operation
+
+long v[PL_MAXDIM]
+int npix, ll_len
+pointer sp, ll_out, ll_reg, rl_out, ll_dst, op
+errchk plvalid, pl_access, pl_linerop, pl_update
+pointer pl_access()
+int pl_r2li()
+
+begin
+ call plvalid (pl)
+ call amovl (PL_PLANE(pl,1), v, PL_MAXDIM)
+ v[2] = y
+
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ call salloc (ll_reg, LL_CURHDRLEN + 6, TY_SHORT)
+ call salloc (rl_out, RL_FIRST * 3, TY_INT)
+
+ # Access the destination line in the mask.
+ ll_dst = pl_access (pl, v)
+
+ # Construct the edit-region list.
+ npix = 1
+ RLI_AXLEN(rl_out) = npix
+ RLI_LEN(rl_out) = RL_FIRST
+
+ op = rl_out + (RL_FIRST - 1) * 3
+ Memi[op+RL_XOFF] = 1
+ Memi[op+RL_NOFF] = npix
+ Memi[op+RL_VOFF] = 1
+
+ ll_len = pl_r2li (Memi[rl_out], 1, Mems[ll_reg], npix)
+
+ # Edit the affected line.
+ call pl_linerop (Mems[ll_reg], 1, 1, Mems[ll_dst], x, PL_MAXVAL(pl),
+ Mems[ll_out], npix, rop)
+
+ # Update the edited line in the mask.
+ call pl_update (pl, v, Mems[ll_out])
+
+ # Compress the mask if excessive free space has accumulated.
+ if (PL_NEEDCOMPRESS(pl))
+ call pl_compress (pl)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plpolygon.h b/sys/plio/plpolygon.h
new file mode 100644
index 00000000..764b32ae
--- /dev/null
+++ b/sys/plio/plpolygon.h
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+
+define TOL 0.0001 # pixel units
+define swapi {tempi=$2;$2=$1;$1=tempi}
+define swapr {tempr=$2;$2=$1;$1=tempr}
+define equal (abs($1-$2)<TOL)
+
+define LEN_PGONDES 7
+define P_PL Memi[$1] # pointer to X vector
+define P_XP Memi[$1+1] # pointer to X vector
+define P_YP Memi[$1+2] # pointer to Y vector
+define P_OO Memi[$1+3] # pointer to previous range list
+define P_OY Memi[$1+4] # y value of previous range list
+define P_NS Memi[$1+5] # number of line segments
+define P_PV Memi[$1+6] # pixel value
diff --git a/sys/plio/plpolygon.x b/sys/plio/plpolygon.x
new file mode 100644
index 00000000..dc249b88
--- /dev/null
+++ b/sys/plio/plpolygon.x
@@ -0,0 +1,71 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plpolygon.h"
+
+
+# PL_POLYGON -- Perform a rasterop operation on the area enclosed by a polygon
+# drawn in a 2-dimensional plane of a mask. If the dimensionality of the mask
+# exceeds 2, the pl_setplane() procedure should be called first to define the
+# plane of the mask to be modified.
+
+procedure pl_polygon (pl, x, y, npts, rop)
+
+pointer pl #I mask descriptor
+int x[npts] #I polygon x-vertices
+int y[npts] #I polygon y-vertices
+int npts #I number of points in polygon
+int rop #I rasterop defining operation
+
+int line_1, line_2, i
+pointer sp, ufd, xp, yp, oo
+extern pl_upolygon()
+errchk plvalid
+
+begin
+ call plvalid (pl)
+ if (npts <= 0)
+ return
+ else if (npts == 1) {
+ call pl_point (pl, x[1], y[1], rop)
+ return
+ }
+
+ call smark (sp)
+ call salloc (ufd, LEN_PGONDES, TY_STRUCT)
+ call salloc (oo, RL_FIRST + (npts+1)*3, TY_INT)
+ call salloc (xp, npts + 1, TY_REAL)
+ call salloc (yp, npts + 1, TY_REAL)
+
+ # Initialize the region descriptor.
+ P_PL(ufd) = pl
+ P_XP(ufd) = xp
+ P_YP(ufd) = yp
+ P_PV(ufd) = 1
+ P_OO(ufd) = oo
+ P_OY(ufd) = -1
+ P_NS(ufd) = npts - 1
+ RLI_LEN(oo) = 0
+
+ # Copy the user supplied polygon vertices into the descriptor,
+ # normalizing the polygon in the process.
+
+ do i = 1, npts {
+ Memr[xp+i-1] = x[i]
+ Memr[yp+i-1] = y[i]
+ }
+
+ if (npts > 2)
+ if (abs(x[1]-x[npts]) > TOL || abs(y[1]-y[npts]) > TOL) {
+ Memr[xp+npts] = x[1]
+ Memr[yp+npts] = y[1]
+ P_NS(ufd) = npts
+ }
+
+ # Compute the range in Y in which the polygon should be drawn.
+ call alimi (y, npts, line_1, line_2)
+
+ call pl_regionrop (pl, pl_upolygon, ufd, line_1, line_2, rop)
+ call sfree (sp)
+end
diff --git a/sys/plio/plprop.gx b/sys/plio/plprop.gx
new file mode 100644
index 00000000..60d1c603
--- /dev/null
+++ b/sys/plio/plprop.gx
@@ -0,0 +1,177 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_PIXROP -- Rasterop between source and destination pixel arrays.
+
+procedure pl_pixrop$t (px_src,xs,src_maxval, px_dst,ds,dst_maxval, npix, rop)
+
+PIXEL px_src[ARB] #I source pixel array
+int xs #I starting pixel index in src
+int src_maxval #I max pixel value in src mask
+PIXEL px_dst[ARB] #O destination pixel array
+int ds #I starting pixel index in dst
+int dst_maxval #I max pixel value in dst mask
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+pointer sp, src
+int opcode, i
+PIXEL data, ceil, src_value
+int and(), or(), xor(), not()
+define out_ 91
+
+begin
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+ ceil = 0
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Handle the easy cases first.
+ switch (opcode) {
+ case PIX_CLR:
+ call aclr$t (px_dst[ds], npix)
+ return
+ case PIX_SET:
+ call amovk$t (data, px_dst[ds], npix)
+ goto out_
+ case PIX_SRC:
+ if (src_maxval != 1)
+ call amov$t (px_src[xs], px_dst[ds], npix)
+ else {
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ px_dst[ds+i-1] = src_value
+ else
+ px_dst[ds+i-1] = 0
+ }
+
+ goto out_
+ case PIX_DST:
+ return # no-op
+ }
+
+ # Integer or boolean source mask?
+ if (src_maxval != 1) {
+ # Integer source mask; operate directly on source mask.
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_src[xs+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (px_src[xs+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(px_src[xs+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(px_src[xs+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (px_src[xs+i-1], px_dst[ds+i-1]))
+ }
+
+ } else {
+ # Boolean source mask; use integer DATA value from ROP if source
+ # mask pixel is set.
+
+ call smark (sp)
+ call salloc (src, npix, TY_PIXEL)
+
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ Mem$t[src+i-1] = src_value
+ else
+ Mem$t[src+i-1] = 0
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (Mem$t[src+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Mem$t[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Mem$t[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (Mem$t[src+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Mem$t[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Mem$t[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(Mem$t[src+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(Mem$t[src+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (Mem$t[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (Mem$t[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (Mem$t[src+i-1], px_dst[ds+i-1]))
+ }
+
+ call sfree (sp)
+ }
+out_
+ # If writing to an integer mask, mask the data to the indicated max
+ # value (necessary to avoid very large values if any NOT operations
+ # occurred). If writing to a boolean mask, map positive integer mask
+ # values to 1.
+
+ if (dst_maxval == 1) {
+ data = 1
+ call argt$t (px_dst[ds], npix, ceil, data)
+ } else if (dst_maxval > 1) {
+ data = dst_maxval
+ call aandk$t (px_dst[ds], data, px_dst[ds], npix)
+ }
+end
diff --git a/sys/plio/plr2l.gx b/sys/plio/plr2l.gx
new file mode 100644
index 00000000..d0a98e92
--- /dev/null
+++ b/sys/plio/plr2l.gx
@@ -0,0 +1,130 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2L -- Convert a range list to a line list. The length of the output
+# line list is returned as the function value.
+
+int procedure pl_r2l$t (rl_src, xs, ll_dst, npix)
+
+PIXEL rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+PIXEL hi, pv
+int last, xe, x1, x2, iz, op, np, nz, nr, dv, v, i
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+ iz = xs
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr + 1 {
+ if (i <= nr) {
+ # Load next range.
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+ last = x2
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ # Go again if nothing inbounds.
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ } else if (iz < xe) {
+ # At end of input range list, but need to output a ZN.
+ nz = xe - iz + 1
+ np = 0
+ pv = 0
+ } else
+ break
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0 && x2 == last) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ iz = x2 + 1
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/plr2p.gx b/sys/plio/plr2p.gx
new file mode 100644
index 00000000..fa0eecda
--- /dev/null
+++ b/sys/plio/plr2p.gx
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2P -- Convert a range list to a pixel array. The number of pixels
+# output (always npix) is returned as the function value.
+
+int procedure pl_r2p$t (rl_src, xs, px_dst, npix)
+
+PIXEL rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+PIXEL px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+PIXEL hi, pv
+int xe, x1, x2, iz, op, np, nz, nr, i, j
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ iz = xs
+ op = 1
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr {
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ do j = 1, nz
+ px_dst[op+j-1] = 0
+ op = op + nz
+ }
+
+ # Output the pixels.
+ do j = 1, np
+ px_dst[op+j-1] = pv
+ op = op + np
+done_
+ x1 = x2 + 1
+ iz = x1
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/plregrop.x b/sys/plio/plregrop.x
new file mode 100644
index 00000000..6e37183a
--- /dev/null
+++ b/sys/plio/plregrop.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_REGIONROP -- Perform a rasterop operation on an irregular region in a
+# 2-dimensional plane of a mask. The boundary of the region is defined by
+# a user supplied function which, given the line number (Y) returns a range
+# list defining the region for that line.
+#
+# rl_new = ufcn (ufd, y, rl_out, xs, npix)
+#
+# where
+# rl_new = true if range list for line Y is different than for Y-1
+# ufd = user region descriptor (parameters defining region)
+# y = input y value
+# rl_out = output range list
+# xs = first pixel to be edited in dst mask
+# npix = number of pixels in edit region
+#
+# If the dimensionality of the mask exceeds 2, the pl_setplane() procedure
+# should be called first to define the plane of the mask to be modified.
+
+procedure pl_regionrop (pl, ufcn, ufd, y1, y2, rop)
+
+pointer pl #I mask descriptor
+extern ufcn() #I user supplied region tracing procedure
+pointer ufd #I user region descriptor
+int y1 #I first mask line to be modified
+int y2 #I last mask line to be modified
+int rop #I rasterop defining operation
+
+bool rl_new
+long v[PL_MAXDIM]
+int ll_len, xs, npix
+pointer sp, ll_out, ll_reg, ll_dst, ol_dst, rl_out
+pointer pl_access()
+int pl_r2li()
+bool ufcn()
+errchk plvalid
+
+begin
+ call plvalid (pl)
+
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ call salloc (ll_reg, LL_MAXLEN(pl), TY_SHORT)
+ call salloc (rl_out, RL_MAXLEN(pl), TY_INT)
+
+ call amovl (PL_PLANE(pl,1), v, PL_MAXDIM)
+ ol_dst = 1
+
+ for (v[2]=y1; v[2] <= y2; v[2]=v[2]+1) {
+ ll_dst = pl_access (pl, v)
+ rl_new = ufcn (ufd, v[2], Memi[rl_out], xs, npix)
+ if (rl_new)
+ ll_len = pl_r2li (Memi[rl_out], 1, Mems[ll_reg], npix)
+
+ if (ll_dst != ol_dst || rl_new) {
+ call pl_linestencil (Mems[ll_reg], 1, 1,
+ Mems[ll_dst], xs, PL_MAXVAL(pl),
+ Mems[ll_reg], 1,
+ Mems[ll_out], npix, rop)
+ ol_dst = ll_dst
+ }
+
+ # Update the affected line of the destination mask.
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ # Compress the mask if excessive free space has accumulated.
+ if (PL_NEEDCOMPRESS(pl))
+ call pl_compress (pl)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plreq.gx b/sys/plio/plreq.gx
new file mode 100644
index 00000000..470b4484
--- /dev/null
+++ b/sys/plio/plreq.gx
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+
+# PLR_EQUAL -- Compare two range lists for equality.
+
+bool procedure plr_equal$t (r1, r2)
+
+PIXEL r1[3,ARB] #I range list 1
+PIXEL r2[3,ARB] #I range list 2
+
+int i
+int len1, len2
+
+begin
+ len1 = RL_LEN(r1)
+ len2 = RL_LEN(r2)
+
+ if (len1 != len2)
+ return (false)
+
+ do i = RL_FIRST, len1
+ if (r1[1,i] != r2[1,i] || r1[2,i] != r2[2,i] || r1[3,i] != r2[3,i])
+ return (false)
+
+ return (true)
+end
diff --git a/sys/plio/plrio.x b/sys/plio/plrio.x
new file mode 100644
index 00000000..0cf2b8d9
--- /dev/null
+++ b/sys/plio/plrio.x
@@ -0,0 +1,350 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plio.h>
+
+.help PLRIO
+.nf ---------------------------------------------------------------------------
+PLRIO -- A small package used to provide a means for efficient random
+sampling (at the pixel level) of large PLIO masks. In other words, if we have
+a large mask and want to determine the values of successive mask pixels at
+random locations in the mask, this package provides a more efficient means
+for doing so than calling a routine such as PL_GLPI. The mask must already
+exist; means are not provided within this package for creating or editing
+masks, only for reading them.
+
+ plr = plr_open (pl, plane, buflimit)
+ plr_setrect (plr, x1,y1, x2,y2)
+ mval = plr_getpix (plr, x, y)
+ plr_getlut (plr, bufp, xsize,ysize, xblock,yblock)
+ plr_close (plr)
+
+PLR_OPEN opens the indicated 2 dimensional plane of the N dimensional mask PL.
+Buffer space used to provide an efficient means of randomly sampling the mask
+will be kept to within approximately BUFLIMIT integer units of storage (the
+internal table used to sample the mask is type integer, so BUFLIMIT is the
+approximate number of entries in the table). Random sampling of the mask is
+provided by the integer function PLR_GETPIX, which returns the mask value at
+the point [i,j] within the specified plane. PLR_SETRECT may be called before
+calling PLR_GETPIX to set the clipping rectangle, which defaults to the
+boundaries of the mask. If a PLR_GETPIX call references outside the clipping
+region, ERR will be returned as the mask value (normal mask values are >= 0).
+Use of a clipping region other than the boundaries of the full mask can avoid
+the need for redundant clipping operations in the client. For cases when
+even the function call overhead of PLR_GETPIX is too much, the lookup table
+may be directly accessed via a call to PLR_GETLUT. Table references which
+resolve to a negative valued table entry should call PLR_GETPIX to get the
+mask value, otherwise the table value is the mask value. PLR_CLOSE should
+be called to free the PLRIO table space (which can be extensive) when no longer
+needed.
+.endhelp ----------------------------------------------------------------------
+
+define DEF_BUFLIMIT (128*128) # internal buffer size
+define LEN_STACK (4*32) # max mask size = 2**LEN_STACK
+
+# If any of the following are changed check that pmio$pmrio.x is consistent.
+define LEN_PLRDES 20
+define PLR_PL Memi[$1] # main PLIO descriptor
+define PLR_NCOLS Memi[$1+1] # table width
+define PLR_NLINES Memi[$1+2] # table height
+define PLR_XBLOCK Memi[$1+3] # table blocking factor, X
+define PLR_YBLOCK Memi[$1+4] # table blocking factor, Y
+define PLR_BUFP Memi[$1+5] # buffer pointer
+define PLR_X1 Memi[$1+6] # clipping rectangle
+define PLR_Y1 Memi[$1+7] # clipping rectangle
+define PLR_X2 Memi[$1+8] # clipping rectangle
+define PLR_Y2 Memi[$1+9] # clipping rectangle
+define PLR_PLANE Memi[$1+10+($2)-1] # plane to be accessed
+
+define COMPLEX -1 # table bin -> compex region
+define LEN_REGDES 4 # region descriptor
+define V1 Memi[$1+($2)-1]
+define V2 Memi[$1+2+($2)-1]
+
+
+# PLR_OPEN -- Open a PLIO mask for random pixel access. Provides efficient
+# random pixel level access to any size mask. This is a 2-dimensional
+# operator, but can be used to sample any 2-dim plane of an N-dim mask.
+
+pointer procedure plr_open (pl, plane, buflimit)
+
+pointer pl #I PLIO descriptor
+int plane[ARB] #I 2-dim plane to be accessed
+int buflimit #I approximate table size, or 0 if don't care
+
+int v1[PL_MAXDIM], v2[PL_MAXDIM]
+int maxpix, ndim, npix, mval, i, j
+int msize[2], tsize[2], block[2], vm[2]
+pointer sp, stack, plr, bufp, el, rp
+errchk calloc, malloc, plvalid
+bool pl_sectnotconst()
+
+begin
+ call plvalid (pl)
+ call smark (sp)
+ call salloc (stack, LEN_STACK * LEN_REGDES, TY_STRUCT)
+
+ # Allocate the PLRIO descriptor.
+ call calloc (plr, LEN_PLRDES, TY_STRUCT)
+
+ # Set the plane to be accessed.
+ ndim = PL_NAXES(pl)
+ do i = 1, 2
+ msize[i] = PL_AXLEN(pl,i)
+
+ do i = 1, PL_MAXDIM
+ if (i > ndim) {
+ PLR_PLANE(pl,i) = 1
+ v1[i] = 1
+ v2[i] = 1
+ } else if (i > 2) {
+ PLR_PLANE(pl,i) = plane[i]
+ v1[i] = plane[i]
+ v2[i] = plane[i]
+ }
+
+ # Get the maximum table size in pixels.
+ if (buflimit <= 0)
+ maxpix = DEF_BUFLIMIT
+ else
+ maxpix = buflimit
+
+ # Determine the blocking factors required to keep the lookup table
+ # within the given size limit.
+
+ block[1] = 1; block[2] = 1
+ while ((msize[1] / block[1]) * (msize[2] / block[2]) > maxpix)
+ do i = 1, 2
+ block[i] = min (msize[i], block[i]*2)
+
+ # Compute the lookup table size.
+ do i = 1, 2
+ tsize[i] = (msize[i] + block[i]-1) / block[i]
+
+ # Allocate the table space.
+ call malloc (bufp, tsize[1] * tsize[2], TY_INT)
+
+ # Compute the lookup table. Since the lookup table can be large,
+ # e.g., a quarter million elements for a 512sq table, we don't want
+ # to directly compute the value of each bufp[i,j]. Instead, we examine
+ # a region of the table, starting with the entire table, and if the
+ # corresponding region of the mask is not filled with the same mask
+ # value, we divide the region into 4 quadrants and examine each in
+ # turn, and so on until the nonconstant regions are the size of one
+ # table bin (pixel), which we conclude maps to a COMPLEX (nonconstant)
+ # region of the mask. By this technique, only table bins which map
+ # to complex mask regions need be evaluated, and entire large regions
+ # of the mask are quickly dealt with.
+
+ # Push the entire mask area on the stack as the first region.
+ el = stack
+ do i = 1, 2 {
+ V1(el,i) = 1
+ V2(el,i) = tsize[i]
+ }
+
+ repeat {
+ # Get the mask coordinates of the next region on the stack.
+ do i = 1, 2 {
+ v1[i] = (V1(el,i) - 1) * block[i] + 1
+ v2[i] = min (msize[i], V2(el,i) * block[i])
+ }
+
+ # Examine the region to see if the associated region of the mask
+ # consists entirely of a single mask value.
+
+ if (pl_sectnotconst (pl, v1, v2, ndim, mval)) {
+ if (V1(el,1) == V2(el,1) && V1(el,2) == V2(el,2)) {
+ # This single table pixel maps to a complex mask region.
+ Memi[bufp+(V1(el,2)-1)*tsize[1]+V1(el,1)-1] = COMPLEX
+
+ } else {
+ # Divide the nonzero mask region into four quadrants
+ # and recursively examine each in turn.
+
+ # Compute the coordinates of the central pixel in vm.
+ do i = 1, 2
+ vm[i] = (V1(el,i) + V2(el,i) + 1) / 2
+
+ # Save the currently stacked region in v1/v2.
+ v1[1] = V1(el,1); v1[2] = V1(el,2)
+ v2[1] = V2(el,1); v2[2] = V2(el,2)
+
+ if ((el-stack)/LEN_REGDES+4 >= LEN_STACK)
+ call syserrs (SYS_PLSTKOVFL, "plr_open")
+
+ # Push the four quadrants of this region on the stack.
+ # If the region we are subdividing is only one pixel
+ # wide in either axis then only two of the regions will
+ # be valid. The invalid regions will have zero pixels
+ # in one axis or the other, i.e. (v2[i] < v1[i]). If
+ # a region is invalid discard it by not advancing the
+ # stack pointer.
+
+ V1(el,1) = v1[1]; V1(el,2) = vm[2]
+ V2(el,1) = vm[1]-1; V2(el,2) = v2[2]
+ if (V1(el,1) <= V2(el,1) && V1(el,2) <= V2(el,2))
+ el = el + LEN_REGDES
+
+ V1(el,1) = vm[1]; V1(el,2) = vm[2]
+ V2(el,1) = v2[1]; V2(el,2) = v2[2]
+ if (V1(el,1) <= V2(el,1) && V1(el,2) <= V2(el,2))
+ el = el + LEN_REGDES
+
+ V1(el,1) = v1[1]; V1(el,2) = v1[2]
+ V2(el,1) = vm[1]-1; V2(el,2) = vm[2]-1
+ if (V1(el,1) <= V2(el,1) && V1(el,2) <= V2(el,2))
+ el = el + LEN_REGDES
+
+ V1(el,1) = vm[1]; V1(el,2) = v1[2]
+ V2(el,1) = v2[1]; V2(el,2) = vm[2]-1
+ if (V1(el,1) <= V2(el,1) && V1(el,2) <= V2(el,2))
+ el = el + LEN_REGDES
+ }
+ } else {
+ # Set entire region to a constant mask value.
+ npix = V2(el,1) - V1(el,1) + 1
+ do j = V1(el,2), V2(el,2) {
+ rp = bufp + (j-1) * tsize[1] + V1(el,1) - 1
+ if (npix == 1) {
+ Memi[rp] = mval
+ } else if (npix < 8) {
+ do i = 1, npix
+ Memi[rp+i-1] = mval
+ } else {
+ if (mval == 0)
+ call aclri (Memi[rp], npix)
+ else
+ call amovki (mval, Memi[rp], npix)
+ }
+ }
+ }
+
+ # Pop stack.
+ el = el - LEN_REGDES
+
+ } until (el < stack)
+
+ # Initialize the PLRIO descriptor.
+ PLR_PL(plr) = pl
+ PLR_NCOLS(plr) = tsize[1]
+ PLR_NLINES(plr) = tsize[2]
+ PLR_XBLOCK(plr) = block[1]
+ PLR_YBLOCK(plr) = block[2]
+ PLR_BUFP(plr) = bufp
+ PLR_X1(plr) = 1
+ PLR_Y1(plr) = 1
+ PLR_X2(plr) = msize[1]
+ PLR_Y2(plr) = msize[2]
+
+ call sfree (sp)
+ return (plr)
+end
+
+
+# PLR_GETPIX -- Return the value of the given mask pixel, identified by the
+# 2-dim coordinates of the pixel relative to the plane of the N-dim mask
+# specified at open time.
+
+int procedure plr_getpix (plr, i, j)
+
+pointer plr #I PLR descriptor
+int i, j #I plane-relative coordinates of pixel
+
+pointer pl, ll_src
+int ii, jj, mval, np
+pointer pl_access()
+int pl_l2pi()
+errchk pl_access
+
+begin
+ # Clip to the specified region of the mask.
+ if (i < PLR_X1(plr) || i > PLR_X2(plr))
+ return (ERR)
+ if (j < PLR_Y1(plr) || j > PLR_Y2(plr))
+ return (ERR)
+
+ # Map mask pixel coordinates to lookup table bin.
+ ii = (i - 1) / PLR_XBLOCK(plr)
+ jj = (j - 1) / PLR_YBLOCK(plr)
+
+ # Get the lookup table value of the given bin.
+ mval = Memi[PLR_BUFP(plr)+jj*PLR_NCOLS(plr)+ii]
+
+ # Access the original mask to get value if complex region.
+ if (mval == COMPLEX) {
+ pl = PLR_PL(plr)
+ PLR_PLANE(plr,2) = j
+ ll_src = pl_access (pl, PLR_PLANE(plr,1))
+ np = pl_l2pi (Mems[ll_src], i, mval, 1)
+ }
+
+ return (mval)
+end
+
+
+# PLR_GETLUT -- Obtain the buffer pointer and scaling information of the
+# internal lookup table, so that direct table references may be made to
+# minimize overhead in particularly demanding applications. This is not
+# recommended unless absolutely necessary, as PLR_GETPIX is easier and
+# safer to use and nearly as efficient. The strategy for using the table
+# is to use the blocking factors and XSIZE to map a 2dim mask coordinate
+# into a table offset, and access the table to get the table value.
+# If this is negative PLR_GETPIX should be called to compute the mask
+# value, else the table value is the mask value.
+
+procedure plr_getlut (plr, bufp, xsize,ysize, xblock,yblock)
+
+pointer plr #I PLR descriptor
+pointer bufp #O lookup table buffer pointer (int *)
+int xsize,ysize #O table size
+int xblock,yblock #O blocking factors
+
+begin
+ bufp = PLR_BUFP(plr)
+ xsize = PLR_NCOLS(plr)
+ ysize = PLR_NLINES(plr)
+ xblock = PLR_XBLOCK(plr)
+ yblock = PLR_YBLOCK(plr)
+end
+
+
+# PLR_SETRECT -- Set the clipping region for PLR_GETPIX.
+
+procedure plr_setrect (plr, x1,y1, x2,y2)
+
+pointer plr #I PLR descriptor
+int x1,y1 #I lower left corner of region
+int x2,y2 #I upper right corner of region
+
+pointer pl
+define oob_ 91
+errchk syserrs
+
+begin
+ pl = PLR_PL(plr)
+
+ if (x1 < 1 || x1 > PL_AXLEN(pl,1))
+ goto oob_
+ if (x2 < 1 || x2 > PL_AXLEN(pl,1))
+ goto oob_
+ if (y1 < 1 || y1 > PL_AXLEN(pl,2))
+ goto oob_
+ if (y2 < 1 || y2 > PL_AXLEN(pl,2))
+oob_ call syserrs (SYS_PLREFOOB, "plr_setrect")
+
+ PLR_X1(plr) = x1; PLR_Y1(plr) = y1
+ PLR_X2(plr) = x2; PLR_Y2(plr) = y2
+end
+
+
+# PLR_CLOSE -- Free a PLRIO descriptor.
+
+procedure plr_close (plr)
+
+pointer plr #I PLR descriptor
+
+begin
+ call mfree (PLR_BUFP(plr), TY_INT)
+ call mfree (plr, TY_STRUCT)
+end
diff --git a/sys/plio/plrop.x b/sys/plio/plrop.x
new file mode 100644
index 00000000..b111a359
--- /dev/null
+++ b/sys/plio/plrop.x
@@ -0,0 +1,93 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_ROP -- Perform a rasterop operation from the source mask to the
+# destination mask at the given offsets. The source and destination need
+# not be the same size or dimensionality, but out of bounds references are
+# not permitted. If the source is of lesser dimensionality than the
+# indicated section of the destination, then the source will be rewound
+# and reread as necessary to operate upon the entire destination subregion,
+# e.g., a line source mask may be applied to successive lines of a plane,
+# or a plane mask may be applied to successive planes of a 3D mask.
+# The source and destination masks may be the same if desired, but if the
+# source and destination regions overlap feedback may occur (this could be
+# fixed). With some rasterops, e.g, PIX_SET or PIX_CLR, no source mask is
+# required, and pl_src=NULL is permitted.
+
+procedure pl_rop (pl_src, vs_src, pl_dst, vs_dst, vn, rop)
+
+pointer pl_src #I source mask or NULL
+long vs_src[PL_MAXDIM] #I start vector in source mask
+pointer pl_dst #I destination mask (required)
+long vs_dst[PL_MAXDIM] #I start vector in destination mask
+long vn[PL_MAXDIM] #I vector giving subregion size
+long rop #I rasterop
+
+bool need_src
+pointer sp, ll_out, ll_src, ll_dst, ol_src, ol_dst
+long v_src[PL_MAXDIM], v_dst[PL_MAXDIM]
+long ve_src[PL_MAXDIM], ve_dst[PL_MAXDIM]
+
+int plloop()
+pointer pl_access()
+errchk syserr, plvalid, plsslv, pl_access
+
+begin
+ call plvalid (pl_dst)
+ need_src = R_NEED_SRC(rop)
+ if (need_src && pl_src == NULL)
+ call syserr (SYS_PLNULLSRC)
+
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl_dst), TY_SHORT)
+
+ # Initialize the N-dimensional loop counters.
+ call plsslv (pl_dst, vs_dst, vn, v_dst, ve_dst)
+ if (need_src)
+ call plsslv (pl_src, vs_src, vn, v_src, ve_src)
+ else
+ ll_src = ll_out # any valid pointer will do
+
+ # Perform the operation.
+ ol_dst = -1
+ repeat {
+ # Get a line from each mask. The DST linelist is required,
+ # even if R_NEED_DST(rop) is false, because the DST size
+ # parameters determine the size of the output list, and the
+ # rop may only apply to a portion of the DST list.
+
+ ll_dst = pl_access (pl_dst, v_dst)
+ if (need_src)
+ ll_src = pl_access (pl_src, v_src)
+
+ # Perform the rasterop operation upon one line of the mask.
+ # Note that if successive mask lines point to the same encoded
+ # line list, we only have to compute the result once.
+
+ if (ll_src != ol_src || ll_dst != ol_dst) {
+ call pl_linerop (Mems[ll_src], vs_src[1], PL_MAXVAL(pl_src),
+ Mems[ll_dst], vs_dst[1], PL_MAXVAL(pl_dst),
+ Mems[ll_out], vn[1], rop)
+ ol_src = ll_src
+ ol_dst = ll_dst
+ }
+
+ # Update the affected line of the destination mask.
+ call pl_update (pl_dst, v_dst, Mems[ll_out])
+
+ # If the end of the input mask is reached, rewind it and go again.
+ if (need_src)
+ if (plloop (v_src,vs_src,ve_src,PL_NAXES(pl_src)) == LOOP_DONE)
+ call amovi (vs_src, v_src, PL_NAXES(pl_src))
+
+ } until (plloop (v_dst, vs_dst, ve_dst, PL_NAXES(pl_dst)) == LOOP_DONE)
+
+ # Compress the mask if excessive free space has accumulated.
+ if (PL_NEEDCOMPRESS(pl_dst))
+ call pl_compress (pl_dst)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plrpr.gx b/sys/plio/plrpr.gx
new file mode 100644
index 00000000..f0004515
--- /dev/null
+++ b/sys/plio/plrpr.gx
@@ -0,0 +1,56 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PLR_PRINT -- Print a range list on the given output stream.
+
+procedure plr_print$t (rl, fd, label, firstcol, maxcol)
+
+PIXEL rl[3,ARB] #I range list
+int fd #I output file
+char label[ARB] #I line label
+int firstcol #I first column for output
+int maxcol #I width of formatted output
+
+pointer sp, buf
+int col, rn, r_len, x, n, pv
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ # Output the line label and advance to the first column. If the label
+ # extends beyond the first column, start a new line.
+
+ call putline (fd, label)
+ col = strlen (label) + 1
+ if (col > firstcol)
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+
+ r_len = RL_LEN(rl)
+
+ # Decode the range list proper.
+ do rn = RL_FIRST, r_len {
+ x = RL_X(rl,rn)
+ n = RL_N(rl,rn)
+ pv = RL_V(rl,rn)
+
+ if (n == 1) {
+ call sprintf (Memc[buf], SZ_LINE, "%d(%d)")
+ call pargi (x)
+ call pargi (pv)
+ } else {
+ call sprintf (Memc[buf], SZ_LINE, "%d-%d(%d)")
+ call pargi (x)
+ call pargi (x+n-1)
+ call pargi (pv)
+ }
+
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ }
+
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+ call sfree (sp)
+end
diff --git a/sys/plio/plrrop.gx b/sys/plio/plrrop.gx
new file mode 100644
index 00000000..dcc961b2
--- /dev/null
+++ b/sys/plio/plrrop.gx
@@ -0,0 +1,195 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "../plrseg.h"
+
+# PL_RANGEROP -- Rasterop operation between source and destination range lists.
+# The indicated rasterop operation is performed upon the source and destination
+# range lists, writing the result to RL_OUT, which is a copy of RL_DST except
+# for the region affected by the rasterop operation (note that the destination
+# range list cannot be edited in place since it may change size).
+
+procedure pl_rangerop$t (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+
+PIXEL rl_src[3,ARB] #I source range list
+int xs #I starting pixel index in src range list
+int src_maxval #I max pixel value in src mask
+PIXEL rl_dst[3,ARB] #I destination range list
+int ds #I starting pixel index in dst range list
+int dst_maxval #I max pixel value in dst mask
+PIXEL rl_out[3,ARB] #O output list (edited version of rl_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+bool need_src, need_dst, rop_enable
+PIXEL data, src_value, v_src, v_dst, pv
+int segsize, opcode, x, i, np, rn_o, p
+int d_src[LEN_PLRDES], d_dst[LEN_PLRDES]
+
+begin
+ need_src = R_NEED_SRC(rop)
+ need_dst = R_NEED_DST(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Advance to the desired position in the source list, discarding
+ # the unused ranges. The point XS may lie within a range or in a
+ # zero area of the input line.
+
+ if (need_src) {
+ x = 1
+ plr_init (rl_src, d_src)
+ do i = 1, ARB {
+ np = min (plr_nleft(d_src), xs - x)
+ plr_getseg (rl_src, d_src, np, v_src)
+ x = x + np
+ if (x >= xs || np == 0)
+ break
+ }
+ }
+
+ # Advance through both the source and destination lists, extracting
+ # line segments which have a constant value in each input list; the
+ # values for the two lists may differ. Apply the given rasterop to
+ # the source and destination pixel values and write each line segment
+ # as a range to the output list. If the ranges in the two input lists
+ # differ (randomly overlap) then the output list will generally be
+ # more fragmented, i.e., have more ranges of constant value. As each
+ # output range is generated compare it with the previous range to see
+ # if they can be joined, as applying a rasterop may cause two different
+ # ranges to have the same value.
+
+ x = 1
+ rn_o = RL_FIRST
+ segsize = ds - 1
+ rop_enable = false
+ plr_init (rl_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been properly performed
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, plr_nleft(d_dst))
+ if (need_src && rop_enable && plr_nleft(d_src) > 0)
+ np = min (np, plr_nleft(d_src))
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We must
+ # read the DST list whether or not we will use the data, since
+ # the list pointer must be advanced NPIX pixels so that we may
+ # copy the remainder of the list after the loop.
+
+ plr_getseg (rl_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ # Get v_src.
+ if (need_src) {
+ v_src = 0
+ if (plr_nleft (d_src) > 0)
+ plr_getseg (rl_src, d_src, np, v_src)
+
+ if (R_NOTSRC(rop)) {
+ v_src = not (v_src)
+ if (src_maxval != 0)
+ v_src = and (int(v_src), src_maxval)
+ }
+
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ }
+
+ # Get v_dst.
+ if (need_dst) {
+ if (R_NOTDST(rop)) {
+ v_dst = not (v_dst)
+ if (dst_maxval != 0)
+ v_dst = and (int(v_dst), dst_maxval)
+ }
+ }
+
+ # Apply the rasterop.
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC, PIX_NOTSRC:
+ pv = v_src
+ case PIX_DST, PIX_NOTDST:
+ pv = v_dst
+ case PIX_SRC_AND_DST, PIX_SRC_AND_NOTDST, PIX_NOTSRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST, PIX_SRC_OR_NOTDST, PIX_NOTSRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, or map int
+ # to bool for the case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (int(pv), dst_maxval)
+
+ } else
+ pv = v_dst
+
+ # Output a nonzero range.
+ if (pv > 0) {
+ p = rn_o - 1
+ if (p >= RL_FIRST &&
+ pv == rl_out[3,p] && x == rl_out[1,p] + rl_out[2,p]) {
+ # Merge new range with previous one.
+ rl_out[2,p] = rl_out[2,p] + np
+ } else {
+ rl_out[1,rn_o] = x
+ rl_out[2,rn_o] = np
+ rl_out[3,rn_o] = pv
+ rn_o = rn_o + 1
+ }
+ }
+
+ x = x + np
+ segsize = segsize - np
+ }
+
+ # Update the range list header.
+ call amov$t (rl_dst, rl_out, (RL_FIRST - 1) * 3)
+ RL_LEN(rl_out) = rn_o - 1
+end
diff --git a/sys/plio/plrseg.h b/sys/plio/plrseg.h
new file mode 100644
index 00000000..f37372c3
--- /dev/null
+++ b/sys/plio/plrseg.h
@@ -0,0 +1,58 @@
+# PLRSEG.H -- Macros for sequentially reading segments of a range list.
+#
+# plr_init (rl, descriptor)
+# npix = plr_nleft (descriptor)
+# val = plr_getseg (rl, descriptor, npix, value)
+#
+# plr_init Initialize descriptor for sequential i/o from the rangelist RL.
+# plr_nleft Number of pixels left in the current line segment of constant
+# value. Zero is returned at the EOL.
+# plr_getseg Read NPIX pixels from the current segment, advancing to the
+# next segment automatically when the the current segment is
+# exhausted.
+#
+# The descriptor is an integer array, the contents of which are hidden from
+# the application using these macros.
+
+# Range list i/o descriptor.
+define LEN_PLRDES 4
+define rd_nleft $1[1]
+define rd_value $1[2]
+define rd_x $1[3]
+define rd_rn $1[4]
+
+# PLR_INIT -- Initialize the rangelist descriptor.
+define (plr_init, { # $1=rl $2=des
+ rd_x($2) = 1
+ rd_rn($2) = RL_FIRST
+ plr_nextseg ($1, $2)
+})
+
+# PLR_NLEFT -- Number of pixels left in the current segment.
+define plr_nleft rd_nleft($1)
+
+# PLR_GETSEG -- Read pixels from the current segment.
+define (plr_getseg, { # $1=rl $2=des $3=npix $4=value
+ $4 = rd_value($2)
+ rd_x($2) = rd_x($2) + $3
+ rd_nleft($2) = rd_nleft($2) - $3
+ if (rd_nleft($2) <= 0)
+ plr_nextseg ($1, $2)
+})
+
+# PLR_NEXTSEG -- Set up the next segment (internal routine).
+define (plr_nextseg, { # $1=rl $2=des
+ if (rd_rn($2) <= RL_LEN($1)) {
+ if ($1[1,rd_rn($2)] > rd_x($2)) {
+ rd_value($2) = 0
+ rd_nleft($2) = $1[1,rd_x($2)] - rd_x($2)
+ } else {
+ rd_value($2) = $1[3,rd_rn($2)]
+ rd_nleft($2) = $1[2,rd_rn($2)]
+ rd_rn($2) = rd_rn($2) + 1
+ }
+ } else if (rd_x($2) <= RL_AXLEN($1)) {
+ rd_value($2) = 0
+ rd_nleft($2) = RL_AXLEN($1) - rd_x($2) + 1
+ }
+})
diff --git a/sys/plio/plsave.x b/sys/plio/plsave.x
new file mode 100644
index 00000000..1b0101d3
--- /dev/null
+++ b/sys/plio/plsave.x
@@ -0,0 +1,86 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_SAVE -- Save a mask in a buffer in a machine independent format. The user
+# supplied buffer will be resized if necessary to hold the full encoded mask.
+# If a NULL buffer pointer is given, a new buffer will be allocated with the
+# pointer value overwriting the NULL (hence NULL must not be passed as a
+# constant in the argument list). The length of the encoded mask in words is
+# returned as the function value; this value will be less than BUFLEN is the
+# user supplied buffer is larger than it needs to be to store the mask, i.e.,
+# the user buffer is not resized or reallocated if it is large enough to store
+# the mask. The existing contents of the buffer will be overwritten. Multiple
+# calls may be made to checkpoint the mask, allowing rollback to an earlier
+# state.
+
+int procedure pl_save (pl, bp, buflen, flags)
+
+pointer pl #I mask descriptor
+pointer bp #U buffer pointer (to short), or NULL
+int buflen #U buffer length, shorts
+int flags #I not used at present
+
+pointer sp, index, ex, op
+int sz_index, n_buflen
+int pl_p2li()
+pointer coerce()
+errchk malloc, realloc, pl_compress
+
+begin
+ call smark (sp)
+ call salloc (ex, LEN_PLEXTERN, TY_STRUCT)
+ call salloc (index, PL_NLP(pl) * 3 + LL_CURHDRLEN, TY_SHORT)
+
+ # Eliminate any wasted space in the mask, and compute the amount
+ # of space needed to store the compressed mask. Compress the index
+ # first to eliminate wasted space; this can make a big difference
+ # for a sparse or empty mask.
+
+ call pl_compress (pl)
+ sz_index = pl_p2li (PL_LP(pl,1), 1, Mems[index], PL_NLP(pl))
+ #n_buflen = (LEN_PLEXTERN * SZ_STRUCT + PL_LLLEN(pl) * SZ_SHORT +
+ n_buflen = (LEN_PLEXTERN * SZ_MII_INT + PL_LLLEN(pl) * SZ_SHORT +
+ sz_index * SZ_SHORT) / SZ_SHORT
+
+ # Allocate or resize the output buffer.
+ if (bp == NULL) {
+ call malloc (bp, n_buflen, TY_SHORT)
+ buflen = n_buflen
+ } else if (n_buflen > buflen) {
+ call realloc (bp, n_buflen, TY_SHORT)
+ buflen = n_buflen
+ }
+
+ # Encode and output the external format header structure.
+ call aclri (Memi[ex], LEN_PLEXTERN)
+
+ PLE_MAGIC(ex) = PL_MAGIC(pl)
+ PLE_NAXES(ex) = PL_NAXES(pl)
+ PLE_LLOP(ex) = PL_LLOP(pl)
+ PLE_LLLEN(ex) = PL_LLLEN(pl)
+ PLE_MAXLINE(ex) = PL_MAXLINE(pl)
+ PLE_MAXVAL(ex) = PL_MAXVAL(pl)
+ PLE_NLP(ex) = PL_NLP(pl)
+ PLE_NLPX(ex) = sz_index
+ PLE_EXLEN(ex) = n_buflen
+
+ op = bp
+ call amovl (PL_AXLEN(pl,1), PLE_AXLEN(ex,1), PL_MAXDIM)
+ call miipak32 (Memi[ex], Memi[coerce(op,TY_SHORT,TY_INT)],
+ LEN_PLEXTERN, TY_STRUCT)
+ #op = op + (LEN_PLEXTERN * SZ_STRUCT) / SZ_SHORT
+ op = op + (LEN_PLEXTERN * SZ_MII_INT) / SZ_SHORT
+
+ # Append the compressed index...
+ call miipak16 (Mems[index], Mems[op], sz_index, TY_SHORT)
+ op = op + sz_index
+
+ # and the line list buffer.
+ call miipak16 (LL(pl,0), Mems[op], PL_LLLEN(pl), TY_SHORT)
+ op = op + PL_LLLEN(pl)
+
+ call sfree (sp)
+ return (op - bp)
+end
diff --git a/sys/plio/plsavef.x b/sys/plio/plsavef.x
new file mode 100644
index 00000000..25575e5f
--- /dev/null
+++ b/sys/plio/plsavef.x
@@ -0,0 +1,59 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_SAVEF -- Store a mask in external format in a binary file. This simple
+# code permits only one mask per file; more sophisticated mask storage
+# facilities are planned. These will likely obsolete this routine.
+
+procedure pl_savef (pl, fname, title, flags)
+
+pointer pl #I mask descriptor
+char fname[ARB] #I file name
+char title[ARB] #I mask title
+int flags #I save flags
+
+int fd, masklen, buflen, junk
+pointer sp, fullname, extn, bp, sv
+errchk open, pl_save, write, mfree
+int open(), fnextn(), strlen(), pl_save()
+bool strne()
+
+begin
+ call smark (sp)
+ call salloc (sv, LEN_SVDES, TY_STRUCT)
+ call salloc (extn, SZ_FNAME, TY_CHAR)
+ call salloc (fullname, SZ_PATHNAME, TY_CHAR)
+
+ # Add the ".pl" filename extension if not already present.
+ call strcpy (fname, Memc[fullname], SZ_PATHNAME)
+ junk = fnextn (fname, Memc[extn], SZ_FNAME)
+ if (strne (Memc[extn], "pl"))
+ call strcat (".pl", Memc[fullname], SZ_PATHNAME)
+
+ # The update flag is required to allow overwriting an existing mask.
+ if (and (flags, PL_UPDATE) != 0)
+ iferr (call delete (Memc[fullname]))
+ ;
+
+ # Encode the mask.
+ bp = NULL
+ masklen = pl_save (pl, bp, buflen, flags)
+
+ # Set up the savefile descriptor.
+ SV_MAGIC(sv) = PLIO_SVMAGIC
+ SV_TITLELEN(sv) = strlen (title) + 1
+ SV_MASKLEN(sv) = masklen
+
+ # Write the savefile.
+ fd = open (Memc[fullname], NEW_FILE, BINARY_FILE)
+
+ call miiwritei (fd, Memi[sv], LEN_SVDES)
+ call miiwritec (fd, title, SV_TITLELEN(sv))
+ call write (fd, Mems[bp], masklen * SZ_SHORT)
+ call mfree (bp, TY_SHORT)
+
+ call close (fd)
+ call sfree (sp)
+end
diff --git a/sys/plio/plsaveim.x b/sys/plio/plsaveim.x
new file mode 100644
index 00000000..8e1b4b54
--- /dev/null
+++ b/sys/plio/plsaveim.x
@@ -0,0 +1,122 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <imhdr.h>
+include <imset.h>
+include <mach.h>
+include <plset.h>
+include <plio.h>
+
+# PL_SAVEIM -- Save a mask in a conventional image, i.e., convert a mask to
+# an image.
+
+procedure pl_saveim (pl, imname, title, flags)
+
+pointer pl #I mask descriptor
+char imname[ARB] #I image name or section
+char title[ARB] #I mask "title" string
+int flags #I bitflags
+
+bool sampling
+pointer im, px, im_pl, bp
+int npix, naxes, depth, maxdim, mode, i, locstr, locmem
+long v_in[PL_MAXDIM], v_out[PL_MAXDIM], vn[PL_MAXDIM]
+long vs_l[PL_MAXDIM], vs_p[PL_MAXDIM]
+long ve_l[PL_MAXDIM], ve_p[PL_MAXDIM]
+
+pointer immap()
+long clktime()
+int impnli(), imaccess(), imstati(), strlen()
+errchk immap, syserrs, impnli
+
+begin
+ # Open the new output image.
+ mode = NEW_IMAGE
+ if (and (flags, PL_UPDATE) != 0)
+ if (imaccess (imname, 0) == YES)
+ mode = READ_WRITE
+
+ im = immap (imname, mode, 0)
+
+ # Reload the image header from the "title" string, if any.
+ if (strlen(title) > 0) {
+ call zlocva (title, locstr)
+ call zlocva (Memc, locmem)
+ bp = locstr - locmem + 1
+ call im_pmldhdr (im, bp)
+ }
+
+ # Initialize a new image to the size of the mask. If updating an
+ # existing image the sizes must match.
+
+ call pl_gsize (pl, naxes, vn, depth)
+ maxdim = min (IM_MAXDIM, PL_MAXDIM)
+ npix = vn[1]
+
+ if (mode == NEW_IMAGE) {
+ IM_NDIM(im) = naxes
+ IM_PIXTYPE(im) = TY_SHORT
+ if (PL_MAXVAL(pl) > MAX_SHORT)
+ IM_PIXTYPE(im) = TY_INT
+ call amovl (vn, IM_LEN(im,1), maxdim)
+ } else {
+ if (naxes != IM_NDIM(im)) {
+ call imunmap (im)
+ call syserrs (SYS_IMPLSIZE, imname)
+ }
+ do i = 1, naxes
+ if (vn[i] != IM_LEN(im,i)) {
+ call imunmap (im)
+ call syserrs (SYS_IMPLSIZE, imname)
+ }
+ }
+
+ # If the image is already a mask internally, check whether any
+ # subsampling, axis flipping, or axis mapping is in effect.
+ # If so we can't use PLIO to copy the mask section.
+
+ im_pl = imstati (im, IM_PLDES)
+ sampling = false
+
+ if (im_pl != NULL) {
+ call amovkl (long(1), vs_l, maxdim)
+ call amovl (IM_LEN(im,1), ve_l, maxdim)
+ call imaplv (im, vs_l, vs_p, maxdim)
+ call imaplv (im, ve_l, ve_p, maxdim)
+
+ do i = 1, maxdim {
+ vn[i] = ve_l[i] - vs_l[i] + 1
+ if (vn[i] != ve_p[i] - vs_p[i] + 1) {
+ sampling = true
+ break
+ }
+ }
+ }
+
+ # If the source image is already a mask internally and no axis
+ # geometry is in effect in the image section (if any), then we can
+ # use a PLIO rasterop to efficiently copy the mask subsection.
+
+ if (im_pl != NULL && !sampling) {
+ # Copy a mask subsection (or entire image if no section).
+ call pl_rop (pl, vs_l, im_pl, vs_p, vn, PIX_SRC)
+ call pl_compress (im_pl)
+
+ } else {
+ # Copy image pixels. Initialize the vector loop indices.
+ call amovkl (long(1), v_in, maxdim)
+ call amovkl (long(1), v_out, maxdim)
+
+ # Copy the image.
+ while (impnli (im, px, v_out) != EOF) {
+ call pl_glpi (pl, v_in, Memi[px], 0, npix, PIX_SRC)
+ call amovl (v_out, v_in, maxdim)
+ }
+ }
+
+ IM_MIN(im) = 0
+ IM_MAX(im) = PL_MAXVAL(pl)
+ IM_LIMTIME(im) = clktime(0)
+
+ call imunmap (im)
+end
diff --git a/sys/plio/plsectnc.x b/sys/plio/plsectnc.x
new file mode 100644
index 00000000..7aba7fe6
--- /dev/null
+++ b/sys/plio/plsectnc.x
@@ -0,0 +1,113 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include "pllseg.h"
+include <plio.h>
+
+# PL_SECTNOTCONST -- Test whether the indicated mask image section is constant,
+# i.e., all the pixels in the section have the same value. Return this value
+# if the region is constant.
+
+bool procedure pl_sectnotconst (pl_src, v1, v2, ndim, mval)
+
+pointer pl_src #I mask descriptor
+long v1[PL_MAXDIM] #I starting coordinates of section
+long v2[PL_MAXDIM] #I ending coordinates of section
+int ndim #I section dimension
+int mval #O mask value, if constant region
+
+pointer lp
+int ll_src, lval, i
+long v[PL_MAXDIM], vs[PL_MAXDIM], ve[PL_MAXDIM], vn[PL_MAXDIM]
+
+bool pll_const()
+int pl_reference(), plloop()
+errchk plvalid, plsslv, pl_reference
+
+begin
+ call plvalid (pl_src)
+
+ # Initialize the N-dimensional loop counters.
+ do i = 1, PL_MAXDIM
+ if (i <= ndim) {
+ if (v1[i] <= v2[i]) {
+ vs[i] = v1[i]
+ vn[i] = v2[i] - v1[i] + 1
+ } else {
+ vs[i] = v2[i]
+ vn[i] = v1[i] - v2[i] + 1
+ }
+ } else {
+ vs[i] = 1
+ vn[i] = 1
+ }
+
+ call plsslv (pl_src, vs, vn, v, ve)
+ mval = ERR
+
+ # Test each line segment in the section.
+ repeat {
+ # Determine if line segment is complex, or is all set to lval.
+ ll_src = pl_reference (pl_src, v)
+ if (ll_src == PL_EMPTYLINE)
+ lval = 0
+ else {
+ lp = Ref (pl_src, ll_src)
+ if (!pll_const (Mems[lp], vs[1], vn[1], lval))
+ return (true)
+ }
+
+ # Exit if the mask value changes, indicating a complex region.
+ if (mval == ERR)
+ mval = lval
+ else if (mval != lval)
+ return (true)
+
+ } until (plloop (v, vs, ve, PL_NAXES(pl_src)) == LOOP_DONE)
+
+ return (false)
+end
+
+
+# PLL_CONST -- Test whether a section of a line list is set to a constant
+# value. If yes, return the value in mval.
+
+bool procedure pll_const (ll_src, xs, npix, mval)
+
+short ll_src[ARB] #I input line list
+int xs #I first pixel to test
+int npix #I length of region to be tested
+int mval #O mask value, if constant valued segment
+
+int nleft, x1, np, v_src, i
+int d_src[LEN_PLLDES]
+
+begin
+ # Advance to the indicated position in the source list.
+ x1 = 1
+ pll_init (ll_src, d_src)
+ do i = 1, ARB {
+ np = min (pll_nleft(d_src), xs - x1)
+ pll_getseg (ll_src, d_src, np, v_src)
+ x1 = x1 + np
+ if (x1 >= xs || np == 0)
+ break
+ }
+
+ # Test if the next npix pixels are all set to the same value.
+ # Note the line list is segmented and we have to read segments until
+ # we have examined NPIX pixels, or until the mask value changes.
+
+ mval = -1
+ for (nleft=npix; nleft > 0; nleft = nleft - np) {
+ np = min (pll_nleft(d_src), nleft)
+ pll_getseg (ll_src, d_src, np, v_src)
+ if (v_src != mval)
+ if (mval < 0)
+ mval = v_src
+ else
+ return (false)
+ }
+
+ return (true)
+end
diff --git a/sys/plio/plsectne.x b/sys/plio/plsectne.x
new file mode 100644
index 00000000..c8ca8f6d
--- /dev/null
+++ b/sys/plio/plsectne.x
@@ -0,0 +1,101 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include "pllseg.h"
+include <plio.h>
+
+# PL_SECTNOTEMPTY -- Test whether the indicated mask image section is empty.
+
+bool procedure pl_sectnotempty (pl_src, v1, v2, ndim)
+
+pointer pl_src #I mask descriptor
+long v1[PL_MAXDIM] #I starting coordinates of section
+long v2[PL_MAXDIM] #I ending coordinates of section
+int ndim
+
+pointer lp
+int ll_src, i
+long v[PL_MAXDIM], vs[PL_MAXDIM], ve[PL_MAXDIM], vn[PL_MAXDIM]
+
+bool pll_empty()
+int pl_reference(), plloop()
+errchk plvalid, plsslv, pl_reference
+
+begin
+ call plvalid (pl_src)
+
+ # Initialize the N-dimensional loop counters.
+ do i = 1, PL_MAXDIM
+ if (i <= ndim) {
+ if (v1[i] <= v2[i]) {
+ vs[i] = v1[i]
+ vn[i] = v2[i] - v1[i] + 1
+ } else {
+ vs[i] = v2[i]
+ vn[i] = v1[i] - v2[i] + 1
+ }
+ } else {
+ vs[i] = 1
+ vn[i] = 1
+ }
+
+ call plsslv (pl_src, vs, vn, v, ve)
+
+ # Test each line segment in the section.
+ repeat {
+ ll_src = pl_reference (pl_src, v)
+ if (ll_src != PL_EMPTYLINE) {
+ lp = Ref (pl_src, ll_src)
+ if (!pll_empty (Mems[lp], vs[1], vn[1]))
+ return (true)
+ }
+ } until (plloop (v, vs, ve, PL_NAXES(pl_src)) == LOOP_DONE)
+
+ return (false)
+end
+
+
+# PLL_EMPTY -- Test whether a section of a line list is empty.
+
+bool procedure pll_empty (ll_src, xs, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I first pixel to test
+int npix #I length of region to be tested
+
+int nleft, x1, np, v_src, i
+int d_src[LEN_PLLDES]
+
+begin
+ # Advance to the indicated position in the source list.
+ x1 = 1
+ pll_init (ll_src, d_src)
+ do i = 1, ARB {
+ np = min (pll_nleft(d_src), xs - x1)
+ pll_getseg (ll_src, d_src, np, v_src)
+ x1 = x1 + np
+ if (x1 >= xs || np == 0)
+ break
+ }
+
+ # Test if the next npix pixels are zero.
+ if (pll_nleft(d_src) < npix)
+ return (false)
+ else {
+ pll_getseg (ll_src, d_src, npix, v_src)
+ return (v_src == 0)
+ }
+
+ # Test if the next npix pixels are zero. Note the line list is
+ # segmented and we have to read segments until we have examined NPIX
+ # pixels, or until a nonzero mask pixel is encountered.
+
+ for (nleft=npix; nleft > 0; nleft = nleft - np) {
+ np = min (pll_nleft(d_src), nleft)
+ pll_getseg (ll_src, d_src, np, v_src)
+ if (v_src != 0)
+ return (false)
+ }
+
+ return (true)
+end
diff --git a/sys/plio/plseti.x b/sys/plio/plseti.x
new file mode 100644
index 00000000..c6e9d2b4
--- /dev/null
+++ b/sys/plio/plseti.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_SETI -- Set a PLIO parameter.
+
+procedure pl_seti (pl, param, value)
+
+pointer pl #I mask descriptor
+int param #I parameter code
+int value #I parameter value
+
+begin
+ switch (param) {
+ case P_PRIVATE1:
+ PL_PRIVATE1(pl) = value
+ case P_PRIVATE2:
+ PL_PRIVATE2(pl) = value
+ case P_MAXLINE:
+ PL_MAXLINE(pl) = value
+ case P_DEPTH:
+ PL_MAXVAL(pl) = MV(value)
+ default:
+ call syserr (SYS_PLINVPAR)
+ }
+end
diff --git a/sys/plio/plsplane.x b/sys/plio/plsplane.x
new file mode 100644
index 00000000..e4a1f3c1
--- /dev/null
+++ b/sys/plio/plsplane.x
@@ -0,0 +1,15 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_SETPLANE -- Set the 2-Dim plane to be referenced in calls to the pl_box,
+# pl_circle, etc. geometric region masking operators.
+
+procedure pl_setplane (pl, v)
+
+pointer pl #I mask descriptor
+long v[ARB] #I vector defining plane
+
+begin
+ call amovl (v, PL_PLANE(pl,1), PL_MAXDIM)
+end
diff --git a/sys/plio/plssize.x b/sys/plio/plssize.x
new file mode 100644
index 00000000..c14e893d
--- /dev/null
+++ b/sys/plio/plssize.x
@@ -0,0 +1,64 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_SSIZE -- Set the size of a mask, i.e., given an existing open mask
+# descriptor, create an empty mask with the given dimensionality and size.
+
+procedure pl_ssize (pl, naxes, axlen, depth)
+
+pointer pl #I mask descriptor
+int naxes #I number of axes (dimensionality of mask)
+long axlen[ARB] #I length of each axis
+int depth #I mask depth, bits
+
+int npix, i
+pointer sp, px, lp
+int pl_p2ls()
+errchk malloc, calloc, mfree
+
+begin
+ npix = axlen[1]
+ call smark (sp)
+ call salloc (px, npix, TY_SHORT)
+
+ # Initialize the old descriptor.
+ if (PL_LPP(pl) != NULL)
+ call mfree (PL_LPP(pl), TY_INT)
+ if (PL_LLBP(pl) != NULL)
+ call mfree (PL_LLBP(pl), TY_SHORT)
+ call amovki (1, PL_PLANE(pl,1), PL_MAXDIM)
+
+ # Set up the empty descriptor.
+ PL_NAXES(pl) = naxes
+ if (depth > 0)
+ PL_MAXVAL(pl) = MV(depth)
+ else
+ PL_MAXVAL(pl) = MV(PL_MAXDEPTH)
+ call amovl (axlen, PL_AXLEN(pl,1), naxes)
+ do i = naxes + 1, PL_MAXDIM
+ PL_AXLEN(pl,i) = 1
+
+ # Allocate the line list buffer.
+ PL_MAXLINE(pl) = (axlen[1] * 3) + LL_CURHDRLEN
+ PL_LLLEN(pl) = PL_LLBUFLEN
+ call malloc (PL_LLBP(pl), PL_LLBUFLEN, TY_SHORT)
+ lp = PL_LLBP(pl)
+
+ # Encode the empty line line-list.
+ call aclrs (Mems[px], npix)
+ PL_LLOP(pl) = pl_p2ls (Mems[px], 1, Mems[lp], npix)
+
+ # Set up the initial line list index (all lines empty).
+ PL_NLP(pl) = 1
+ do i = 2, naxes
+ PL_NLP(pl) = PL_NLP(pl) * axlen[i]
+ call calloc (PL_LPP(pl), PL_NLP(pl), TY_INT)
+
+ # Set up the LL header for the empty line.
+ LP_NREFS(lp) = PL_NLP(pl)
+ LP_SETBLEN(lp, PL_LLOP(pl))
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plsslv.x b/sys/plio/plsslv.x
new file mode 100644
index 00000000..bb7b21cb
--- /dev/null
+++ b/sys/plio/plsslv.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PLSSLV -- Given two vectors (VS, VN) defining the starting coordinates and
+# size of an image section, initialize the "loop index" vector V, and the
+# loop-end vector VE.
+
+procedure plsslv (pl, vs, vn, v, ve)
+
+pointer pl #I mask descriptor
+long vs[PL_MAXDIM] #I vector coordinates of start of section
+long vn[PL_MAXDIM] #I vector size of section
+long v[PL_MAXDIM] #O vector for i/o (vector loop index)
+long ve[PL_MAXDIM] #O vector coordinates of end of section
+
+int i
+
+begin
+ do i = 1, PL_NAXES(pl) {
+ v[i] = vs[i]
+ ve[i] = vs[i] + vn[i] - 1
+ }
+end
diff --git a/sys/plio/plstati.x b/sys/plio/plstati.x
new file mode 100644
index 00000000..c6659afa
--- /dev/null
+++ b/sys/plio/plstati.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_STATI -- Stat a PLIO parameter.
+
+int procedure pl_stati (pl, param)
+
+pointer pl #I mask descriptor
+int param #I parameter code
+
+int i
+
+begin
+ switch (param) {
+ case P_PRIVATE1:
+ return (PL_PRIVATE1(pl))
+ case P_PRIVATE2:
+ return (PL_PRIVATE2(pl))
+ case P_MAXLINE:
+ return (PL_MAXLINE(pl))
+ case P_DEPTH:
+ do i = 0, ARB
+ if (2**i > min (I_PVMAX, PL_MAXVAL(pl)))
+ return (i)
+ default:
+ call syserr (SYS_PLINVPAR)
+ }
+end
diff --git a/sys/plio/plsten.x b/sys/plio/plsten.x
new file mode 100644
index 00000000..6397af3d
--- /dev/null
+++ b/sys/plio/plsten.x
@@ -0,0 +1,92 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include <plio.h>
+
+# PL_STENCIL -- Perform a rasterop operation from the source mask to the
+# destination mask at the given offsets, but only within the regions set to
+# one in the stencil mask.
+
+procedure pl_stencil (pl_src, vs_src, pl_dst, vs_dst, pl_stn, vs_stn, vn, rop)
+
+pointer pl_src #I source mask or NULL
+long vs_src[PL_MAXDIM] #I start vector in source mask
+pointer pl_dst #I destination mask (required)
+long vs_dst[PL_MAXDIM] #I start vector in destination mask
+pointer pl_stn #I stencil mask (required)
+long vs_stn[PL_MAXDIM] #I start vector in stencil mask
+long vn[PL_MAXDIM] #I vector giving subregion size
+long rop #I rasterop
+
+bool need_src
+pointer sp, ll_out, ll_src, ll_dst, ll_stn, ol_src, ol_dst, ol_stn
+long v_src[PL_MAXDIM], v_dst[PL_MAXDIM], v_stn[PL_MAXDIM]
+long ve_src[PL_MAXDIM], ve_dst[PL_MAXDIM], ve_stn[PL_MAXDIM]
+
+int plloop()
+pointer pl_access()
+errchk syserr, plvalid, plsslv, pl_access
+
+begin
+ call plvalid (pl_dst)
+ call plvalid (pl_stn)
+ need_src = R_NEED_SRC(rop)
+ if (need_src && pl_src == NULL)
+ call syserr (SYS_PLNULLSRC)
+
+ call smark (sp)
+ call salloc (ll_out, LL_MAXLEN(pl_dst), TY_SHORT)
+
+ # Initialize the N-dimensional loop counters.
+ call plsslv (pl_dst, vs_dst, vn, v_dst, ve_dst)
+ call plsslv (pl_stn, vs_stn, vn, v_stn, ve_stn)
+ if (need_src)
+ call plsslv (pl_src, vs_src, vn, v_src, ve_src)
+ else
+ ll_src = ll_out # any valid pointer will do
+
+ # Perform the operation.
+ ol_dst = -1
+ repeat {
+ # Get a line from each mask.
+ ll_dst = pl_access (pl_dst, v_dst)
+ ll_stn = pl_access (pl_stn, v_stn)
+ if (need_src)
+ ll_src = pl_access (pl_src, v_src)
+
+ # Perform the rasterop operation upon one line of the mask.
+ # Note that if successive mask lines point to the same encoded
+ # line list, we only have to compute the result once.
+
+ if (ll_src != ol_src || ll_dst != ol_dst || ll_stn != ol_stn) {
+ call pl_linestencil (Mems[ll_src], vs_src[1], PL_MAXVAL(pl_src),
+ Mems[ll_dst], vs_dst[1], PL_MAXVAL(pl_dst),
+ Mems[ll_stn], vs_stn[1],
+ Mems[ll_out], vn[1], rop)
+
+ ol_src = ll_src
+ ol_dst = ll_dst
+ ol_stn = ll_stn
+ }
+
+ # Update the affected line of the destination mask.
+ call pl_update (pl_dst, v_dst, Mems[ll_out])
+
+ # If the end of the input mask or stencil is reached,
+ # rewind it and go again.
+
+ if (plloop (v_stn,vs_stn,ve_stn,PL_NAXES(pl_stn)) == LOOP_DONE)
+ call amovi (vs_stn, v_stn, PL_NAXES(pl_stn))
+ if (need_src)
+ if (plloop (v_src,vs_src,ve_src,PL_NAXES(pl_src)) == LOOP_DONE)
+ call amovi (vs_src, v_src, PL_NAXES(pl_src))
+
+ } until (plloop (v_dst, vs_dst, ve_dst, PL_NAXES(pl_dst)) == LOOP_DONE)
+
+ # Compress the mask if excessive free space has accumulated.
+ if (PL_NEEDCOMPRESS(pl_dst))
+ call pl_compress (pl_dst)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/plubox.x b/sys/plio/plubox.x
new file mode 100644
index 00000000..ec3e1f47
--- /dev/null
+++ b/sys/plio/plubox.x
@@ -0,0 +1,45 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plbox.h"
+
+
+# PL_UBOX -- Regionrop ufcn for a box (rectangular) region.
+
+bool procedure pl_ubox (ufd, y, rl_reg, xs, npix)
+
+pointer ufd #I user function descriptor
+int y #I mask line number
+int rl_reg[3,ARB] #O output range list for line Y
+int xs #O start of edit region in dst mask
+int npix #O number of pixels affected
+
+int rn
+bool rl_new
+
+begin
+ rl_new = true
+ rn = RL_FIRST
+
+ if (y >= B_Y1(ufd) && y <= B_Y2(ufd)) {
+ xs = B_X1(ufd)
+ npix = B_X2(ufd) - B_X1(ufd) + 1
+
+ RL_X(rl_reg,rn) = 1
+ RL_N(rl_reg,rn) = npix
+ RL_V(rl_reg,rn) = B_PV(ufd)
+
+ rl_new = (y == B_Y1(ufd))
+ rn = rn + 1
+
+ } else {
+ npix = 0
+ xs = 1
+ }
+
+ RL_LEN(rl_reg) = rn - 1
+ RL_AXLEN(rl_reg) = npix
+
+ return (rl_new)
+end
diff --git a/sys/plio/plucircle.x b/sys/plio/plucircle.x
new file mode 100644
index 00000000..5a23d20a
--- /dev/null
+++ b/sys/plio/plucircle.x
@@ -0,0 +1,54 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plcircle.h"
+
+
+# PL_UCIRCLE -- Regionrop ufcn for a circle (circular region), clipped at
+# the borders of the mask.
+
+bool procedure pl_ucircle (ufd, y, rl_reg, xs, npix)
+
+pointer ufd #I user function descriptor
+int y #I mask line number
+int rl_reg[3,ARB] #O output range list for line Y
+int xs #O first pixel to be edited
+int npix #O number of pixels affected
+
+pointer pl
+real radius, dx, dy
+int rn, axlen, x1, x1_clipped, x2, x2_clipped
+
+begin
+ pl = C_PL(ufd)
+ rn = RL_FIRST
+ axlen = PL_AXLEN(pl,1)
+ radius = C_RADIUS(ufd)
+
+ dy = abs (C_YCEN(ufd) - y)
+ if (dy <= radius) {
+ dx = sqrt (radius**2 - dy**2)
+ x1 = C_XCEN(ufd) - int(dx)
+ x2 = C_XCEN(ufd) + int(dx)
+ x1_clipped = max(1, min(axlen, x1))
+ x2_clipped = max(1, min(axlen, x2))
+
+ xs = x1_clipped
+ npix = x2_clipped - x1_clipped + 1
+
+ RL_X(rl_reg,rn) = 1
+ RL_N(rl_reg,rn) = npix
+ RL_V(rl_reg,rn) = C_PV(ufd)
+ rn = rn + 1
+
+ } else {
+ npix = 0
+ xs = 1
+ }
+
+ RL_LEN(rl_reg) = rn - 1
+ RL_AXLEN(rl_reg) = npix
+
+ return (true)
+end
diff --git a/sys/plio/plupdate.x b/sys/plio/plupdate.x
new file mode 100644
index 00000000..bb90cc34
--- /dev/null
+++ b/sys/plio/plupdate.x
@@ -0,0 +1,158 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <mach.h>
+include <plset.h>
+include <plio.h>
+
+# PL_UPDATE -- Update the indicated line of a mask, i.e., insert a possibly
+# modified line (encoded line list) into an image mask. This is where image
+# compression in the Y direction occurs: if the line to be inserted is the
+# "empty line", or a copy of the adjacent line earlier in the image, then we
+# merely set the new line pointer to point to the line already stored, and
+# increment the reference count for that line. When a new mask is created,
+# all lines point to the "empty line" line list (PL_EMPTYLINE), which has a
+# reference count equal to the number of lines in the mask.
+#
+# A new line may replace an existing line in storage if space permits and the
+# reference count of the existing line is one or less, i.e., the stored line
+# list is used only by the image line being updated. Otherwise the new line
+# list will be appended to the line list buffer, which is resized to a larger
+# size if it overflows. Note that the resize may move the buffer, which is
+# why all line "pointers" are actually offsets into the line list buffer.
+#
+# We do not perform garbage collection on freed lines, rather we keep a count
+# of the total amount of freed space (which cannot be reused), allowing the
+# entire line list to be rebuilt periodically to reclaim the space, when the
+# percentage of wasted space rises to a certain level. Since this can be a
+# relatively expensive operation it is only performed by high level, more
+# macroscopic operators, e.g., following a full mask rasterop, or before
+# writing the mask to external storage. Note also that rebulding the mask
+# may shift the stored line lists about, invalidating any line list pointers
+# that may be cached in the calling procedure. This is another reason why
+# we do not perform mask compression automatically if buffer overflow occurs
+# during a line update.
+
+procedure pl_update (pl, v, ll)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I coordinates of line in the mask
+short ll[ARB] #I encoded line list
+
+pointer o_pp, n_pp
+int totlen, axlen, index, i
+int o_lp, n_lp, o_len, n_len, b_len
+
+bool pll_equal()
+int pl_alloc()
+errchk pl_alloc
+define update_ 91
+define oob_ 92
+
+begin
+ # Compute the index of the line in the line pointer array.
+ if (PL_NAXES(pl) == 2) {
+ # Optimized for case naxes=2.
+ index = v[2]
+ if (index < 1 || index > PL_AXLEN(pl,2))
+ goto oob_
+ } else {
+ # Generalized code.
+ index = 1
+ totlen = 1
+ do i = 2, PL_NAXES(pl) {
+ axlen = PL_AXLEN(pl,i)
+ if (v[i] < 1 || v[i] > axlen)
+ goto oob_
+ index = index + totlen * (v[i] - 1)
+ totlen = totlen * axlen
+ }
+ }
+
+ # Now the pointer to the current stored line list.
+ o_lp = PL_LP(pl,index)
+ o_pp = Ref (pl, o_lp)
+ if (o_pp == NULL)
+ return
+
+ # Has the line been modified?
+ n_len = LL_LEN(ll)
+ if (n_len == LP_LEN(o_pp))
+ if (pll_equal (ll, LL(pl,o_lp)))
+ return
+
+ # Keep track of the number of edits.
+ PL_LLNUPDATES(pl) = PL_LLNUPDATES(pl) + 1
+
+ # Is the new line empty?
+ n_lp = PL_EMPTYLINE
+ n_pp = Ref (pl, n_lp)
+ if (n_len == LP_LEN(n_pp))
+ if (pll_equal (ll, LL(pl,n_lp)))
+ goto update_
+
+ # Is the new line a copy of the adjacent line (Y=Y-1) in the image?
+ # Due to the short integer encoding a maximum of MAX_SHORT references
+ # are allowed per line.
+
+ if (index > 1) {
+ n_lp = PL_LP(pl,index-1)
+ n_pp = Ref (pl, n_lp)
+ if (LP_NREF(n_pp) < MAX_SHORT)
+ if (n_len == LP_LEN(n_pp))
+ if (pll_equal (ll, LL(pl,n_lp)))
+ goto update_
+ }
+
+ # The new line isn't a copy of the empty line or of an adjacent line,
+ # so copy it into the line list buffer.
+
+ b_len = LP_BLEN(o_pp)
+ if (LP_NREF(o_pp) <= 1 && n_len <= b_len && o_lp != PL_EMPTYLINE) {
+ # Overwrite existing line.
+
+ o_len = LP_LEN(o_pp)
+ call amovs (ll, LL(pl,o_lp), n_len)
+ LP_NREFS(o_pp) = 1
+ LP_SETBLEN(o_pp, b_len)
+ PL_LLFREE(pl) = PL_LLFREE(pl) + (o_len - LP_LEN(o_pp))
+ return
+
+ } else {
+ # Add a new line.
+
+ n_lp = pl_alloc (pl, n_len)
+ o_pp = Ref (pl, o_lp)
+ n_pp = Ref (pl, n_lp)
+ call amovs (ll, LL(pl,n_lp), n_len)
+
+ LP_NREFS(n_pp) = 0
+ LP_SETBLEN(n_pp, n_len)
+ PL_LLFREE(pl) = PL_LLFREE(pl) + n_len
+ }
+
+ # We get here only if the new line has already been stored in the
+ # linelist buffer, with a pointer to such in N_LP, and a pointer to
+ # the old line in O_LP. Dereference the old line, which the new line
+ # is no longer associated with, and increase the reference count of
+ # the new line.
+update_
+
+ # Dereference the old line and free the space if it is no longer used.
+ # If the old line buffer is freed we reclaim only LP_LEN words, since
+ # we already reclaimed LP_BLEN-LP_LEN in an earlier edit operation.
+
+ LP_NREFS(o_pp) = LP_NREFS(o_pp) - 1
+ if (LP_NREF(o_pp) == 0 && o_lp != PL_EMPTYLINE)
+ PL_LLFREE(pl) = PL_LLFREE(pl) + LP_LEN(o_pp)
+
+ # Add another reference to the new line.
+ LP_NREFS(n_pp) = LP_NREFS(n_pp) + 1
+ if (LP_NREF(n_pp) == 1 && n_lp != PL_EMPTYLINE)
+ PL_LLFREE(pl) = PL_LLFREE(pl) - LP_BLEN(n_pp)
+
+ PL_LP(pl,index) = n_lp
+ return
+oob_
+ call syserr (SYS_PLREFOOB)
+end
diff --git a/sys/plio/plupolygon.x b/sys/plio/plupolygon.x
new file mode 100644
index 00000000..b60e9bc5
--- /dev/null
+++ b/sys/plio/plupolygon.x
@@ -0,0 +1,223 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "plpolygon.h"
+
+
+# PL_UPOLYGON -- Regionrop ufcn for a general closed polygonal region.
+# (surely there must be a simpler way to code this...)
+
+bool procedure pl_upolygon (ufd, line, rl_reg, xs, npix)
+
+pointer ufd #I user function descriptor
+int line #I mask line number
+int rl_reg[3,ARB] #O output range list for line Y
+int xs #O start of edit region in dst mask
+int npix #O number of pixels affected
+
+pointer xp, yp, pl
+bool rl_new, cross
+int nseg, np, low, rn, i1, i2, ii, i, j
+int tempi, axlen, rl_len, p_prev, p_next
+real tempr, y, y1, y2, x1, x2, p1, p2, p_y, n_y
+
+int btoi()
+bool plr_equali()
+define done_ 91
+
+begin
+ pl = P_PL(ufd)
+ axlen = PL_AXLEN(pl,1)
+ rn = RL_FIRST
+ npix = 0
+ xs = 1
+
+ nseg = P_NS(ufd)
+ xp = P_XP(ufd)
+ yp = P_YP(ufd)
+ y = real(line)
+
+ # Find the point(s) of intersection of the current mask line with
+ # the line segments forming the polygon. Note that the line must
+ # cross a segment to go from inside to outside or vice versa; if a
+ # segment (or vertex) is merely touched it should be drawn, but it
+ # is not a point of crossing.
+
+ do i = 1, nseg {
+ # Locate next and previous line segments.
+ if (i == 1)
+ p_prev = nseg
+ else
+ p_prev = i - 1
+ if (i == nseg)
+ p_next = 1
+ else
+ p_next = i + 2
+
+ # Get endpoints of current segment.
+ x1 = Memr[xp+i-1]; x2 = Memr[xp+i]
+ y1 = Memr[yp+i-1]; y2 = Memr[yp+i]
+ if (y1 > y2) {
+ swapr (x1, x2)
+ swapr (y1, y2)
+ swapi (p_next, p_prev)
+ }
+
+ # Does current line intersect the polygon line segment?
+ if (y > y1-TOL && y < y2+TOL) {
+ p_y = Memr[yp+p_prev-1]
+ n_y = Memr[yp+p_next-1]
+
+ if (y2 - y1 > TOL) {
+ # Single point of intersection.
+ p1 = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1)
+ p2 = p1
+
+ if (equal (p1, x1) && equal (y, y1))
+ cross = ((p_y - y1) < 0)
+ else if (equal (p1, x2) && equal (y, y2))
+ cross = ((n_y - y2) > 0)
+ else
+ cross = true
+
+ } else {
+ # Intersection is entire line segment.
+ p1 = x1; p2 = x2
+ cross = (((p_y - y) * (n_y - y)) < 0)
+ }
+
+ i1 = max(1, min(axlen, nint(p1)))
+ i2 = max(1, min(axlen, nint(p2)))
+ if (i1 > i2)
+ swapi (i1, i2)
+
+ np = i2 - i1 + 1
+ if (np > 0) {
+ RL_X(rl_reg,rn) = i1
+ RL_N(rl_reg,rn) = np
+ RL_V(rl_reg,rn) = btoi(cross)
+ rn = rn + 1
+ }
+ }
+ }
+
+ rl_len = rn - 1
+ if (rl_len <= RL_FIRST)
+ goto done_
+
+ # Sort the line intersection-segments in order of increasing X.
+ do j = RL_FIRST, rl_len {
+ # Get low X value of initial segment.
+ i1 = RL_X(rl_reg,j)
+ np = RL_N(rl_reg,j)
+ i1 = min (i1, i1 + np - 1)
+ low = j
+
+ # Find lowest valued segment in remainder of array.
+ do i = j+1, rl_len {
+ i2 = RL_X(rl_reg,i)
+ np = RL_N(rl_reg,i)
+ i2 = min (i2, i2 + np - 1)
+ if (i2 < i1) {
+ i1 = i2
+ low = i
+ }
+ }
+
+ # Interchange the initial segment and the low segment.
+ if (low != j) {
+ swapi (RL_X(rl_reg,j), RL_X(rl_reg,low))
+ swapi (RL_N(rl_reg,j), RL_N(rl_reg,low))
+ swapi (RL_V(rl_reg,j), RL_V(rl_reg,low))
+ }
+ }
+
+ # Combine any segments which overlap.
+ rn = RL_FIRST
+ do i = RL_FIRST + 1, rl_len {
+ i1 = RL_X(rl_reg,rn)
+ i2 = RL_N(rl_reg,rn) + i1 - 1
+ ii = RL_X(rl_reg,i)
+ if (ii >= i1 && ii <= i2) {
+ i2 = ii + RL_N(rl_reg,i) - 1
+ RL_N(rl_reg,rn) = max (RL_N(rl_reg,rn), i2 - i1 + 1)
+ RL_V(rl_reg,rn) = max (RL_V(rl_reg,rn), RL_V(rl_reg,i))
+ } else {
+ rn = rn + 1
+ RL_X(rl_reg,rn) = RL_X(rl_reg,i)
+ RL_N(rl_reg,rn) = RL_N(rl_reg,i)
+ RL_V(rl_reg,rn) = RL_V(rl_reg,i)
+ }
+ }
+ rl_len = rn
+
+ # Now combine successive pairs of intersections to produce the line
+ # segments to be drawn. If all points are crossing points (where the
+ # image line crosses the polygon boundary) then we draw a line between
+ # the first two points, then the second two points, and so on. Points
+ # where the image line touches the polygon boundary but does not cross
+ # it are plotted, but are not joined with other points to make line
+ # segments.
+
+ rn = RL_FIRST
+ ii = RL_FIRST
+
+ do j = RL_FIRST, rl_len {
+ if (j <= ii && j < rl_len) {
+ next
+
+ } else if (RL_V(rl_reg,ii) == YES) {
+ # Skip a vertext that touches but does not cross.
+ if (RL_V(rl_reg,j) == NO && j < rl_len)
+ next
+
+ # Draw a line between the two crossing points.
+ RL_X(rl_reg,rn) = RL_X(rl_reg,ii)
+ RL_N(rl_reg,rn) = max (RL_N(rl_reg,ii),
+ RL_X(rl_reg,j) + RL_N(rl_reg,j) - RL_X(rl_reg,ii))
+ RL_V(rl_reg,rn) = P_PV(ufd)
+ rn = rn + 1
+ ii = j + 1
+
+ } else {
+ # Plot only the first point.
+ RL_X(rl_reg,rn) = RL_X(rl_reg,ii)
+ RL_N(rl_reg,rn) = RL_N(rl_reg,ii)
+ RL_V(rl_reg,rn) = P_PV(ufd)
+ rn = rn + 1
+
+ if (j >= rl_len && j != ii) {
+ # Plot the second point, if and end of list.
+ RL_X(rl_reg,rn) = RL_X(rl_reg,j)
+ RL_N(rl_reg,rn) = RL_N(rl_reg,j)
+ RL_V(rl_reg,rn) = P_PV(ufd)
+ rn = rn + 1
+ } else
+ ii = j
+ }
+ }
+
+done_
+ # Convert the X values in the range list to be relative to the start
+ # of the list. Compute NPIX, the range in pixels spanned by the range
+ # list.
+
+ rl_len = rn - 1
+ xs = RL_X(rl_reg,RL_FIRST)
+ npix = RL_X(rl_reg,rl_len) + RL_N(rl_reg,rl_len) - xs
+
+ do i = RL_FIRST, rl_len
+ RL_X(rl_reg,i) = RL_X(rl_reg,i) - xs + 1
+
+ RL_LEN(rl_reg) = rl_len
+ RL_AXLEN(rl_reg) = npix
+
+ rl_new = true
+ if (P_OY(ufd) == line - 1)
+ rl_new = !plr_equali (rl_reg, Memi[P_OO(ufd)])
+ call amovi (rl_reg, Memi[P_OO(ufd)], rn - 1)
+ P_OY(ufd) = line
+
+ return (rl_new)
+end
diff --git a/sys/plio/plvalid.x b/sys/plio/plvalid.x
new file mode 100644
index 00000000..1d6482d9
--- /dev/null
+++ b/sys/plio/plvalid.x
@@ -0,0 +1,22 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plio.h>
+
+# PLVALID -- Verify the validity of a mask descriptor.
+
+procedure plvalid (pl)
+
+pointer pl #I open mask descriptor
+errchk syserr
+
+begin
+ if (pl != NULL)
+ if (PL_MAGIC(pl) == PL_MAGICVAL)
+ if (PL_LLBP(pl) == NULL)
+ call syserr (SYS_PLINACTDES)
+ else
+ return
+
+ call syserr (SYS_PLINVDES)
+end
diff --git a/sys/plio/tf/mkpkg b/sys/plio/tf/mkpkg
new file mode 100644
index 00000000..00a4d40e
--- /dev/null
+++ b/sys/plio/tf/mkpkg
@@ -0,0 +1,51 @@
+# Update the type expanded generic files in the PLIO package library.
+
+$checkout libex.a lib$
+$update libex.a
+$checkin libex.a lib$
+$exit
+
+libex.a:
+ plglpi.x <plio.h>
+ plglpl.x <plio.h>
+ plglps.x <plio.h>
+ plglri.x <plio.h> <plset.h>
+ plglrl.x <plio.h> <plset.h>
+ plglrs.x <plio.h> <plset.h>
+ pll2pi.x <plio.h>
+ pll2pl.x <plio.h>
+ pll2ps.x <plio.h>
+ pll2ri.x <plio.h> <plset.h>
+ pll2rl.x <plio.h> <plset.h>
+ pll2rs.x <plio.h> <plset.h>
+ plp2li.x <plio.h>
+ plp2ll.x <plio.h>
+ plp2ls.x <plio.h>
+ plp2ri.x <plio.h> <plset.h>
+ plp2rl.x <plio.h> <plset.h>
+ plp2rs.x <plio.h> <plset.h>
+ plplpi.x <plio.h>
+ plplpl.x <plio.h>
+ plplps.x <plio.h>
+ plplri.x <plio.h>
+ plplrl.x <plio.h>
+ plplrs.x <plio.h>
+ plpropi.x <plio.h> <plset.h>
+ plpropl.x <plio.h> <plset.h>
+ plprops.x <plio.h> <plset.h>
+ plr2li.x <plio.h> <plset.h>
+ plr2ll.x <plio.h> <plset.h>
+ plr2ls.x <plio.h> <plset.h>
+ plr2pi.x <plio.h> <plset.h>
+ plr2pl.x <plio.h> <plset.h>
+ plr2ps.x <plio.h> <plset.h>
+ plreqi.x <plset.h>
+ plreql.x <plset.h>
+ plreqs.x <plset.h>
+ plrpri.x <plio.h> <plset.h>
+ plrprl.x <plio.h> <plset.h>
+ plrprs.x <plio.h> <plset.h>
+ plrropi.x <plio.h> <plset.h> ../plrseg.h
+ plrropl.x <plio.h> <plset.h> ../plrseg.h
+ plrrops.x <plio.h> <plset.h> ../plrseg.h
+ ;
diff --git a/sys/plio/tf/plglpi.x b/sys/plio/tf/plglpi.x
new file mode 100644
index 00000000..3724ed6d
--- /dev/null
+++ b/sys/plio/tf/plglpi.x
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GLP -- Get a line segment as a pixel array, applying the given ROP to
+# combine the pixels with those of the output array.
+
+procedure pl_glpi (pl, v, px_dst, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+int px_dst[ARB] #O output pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int np
+pointer sp, px_out, ll_src
+pointer pl_access()
+int pl_l2pi()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop)) {
+ np = pl_l2pi (Mems[ll_src], v[1], px_dst, npix)
+ return
+ }
+
+ call smark (sp)
+ call salloc (px_out, npix, TY_INT)
+
+ np = pl_l2pi (Mems[ll_src], v[1], Memi[px_out], npix)
+ call pl_pixropi (Memi[px_out], 1, PL_MAXVAL(pl),
+ px_dst, 1, MV(px_depth), npix, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plglpl.x b/sys/plio/tf/plglpl.x
new file mode 100644
index 00000000..6e1632c0
--- /dev/null
+++ b/sys/plio/tf/plglpl.x
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GLP -- Get a line segment as a pixel array, applying the given ROP to
+# combine the pixels with those of the output array.
+
+procedure pl_glpl (pl, v, px_dst, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+long px_dst[ARB] #O output pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int np
+pointer sp, px_out, ll_src
+pointer pl_access()
+int pl_l2pl()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop)) {
+ np = pl_l2pl (Mems[ll_src], v[1], px_dst, npix)
+ return
+ }
+
+ call smark (sp)
+ call salloc (px_out, npix, TY_LONG)
+
+ np = pl_l2pl (Mems[ll_src], v[1], Meml[px_out], npix)
+ call pl_pixropl (Meml[px_out], 1, PL_MAXVAL(pl),
+ px_dst, 1, MV(px_depth), npix, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plglps.x b/sys/plio/tf/plglps.x
new file mode 100644
index 00000000..728beb0a
--- /dev/null
+++ b/sys/plio/tf/plglps.x
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_GLP -- Get a line segment as a pixel array, applying the given ROP to
+# combine the pixels with those of the output array.
+
+procedure pl_glps (pl, v, px_dst, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short px_dst[ARB] #O output pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int np
+pointer sp, px_out, ll_src
+pointer pl_access()
+int pl_l2ps()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop)) {
+ np = pl_l2ps (Mems[ll_src], v[1], px_dst, npix)
+ return
+ }
+
+ call smark (sp)
+ call salloc (px_out, npix, TY_SHORT)
+
+ np = pl_l2ps (Mems[ll_src], v[1], Mems[px_out], npix)
+ call pl_pixrops (Mems[px_out], 1, PL_MAXVAL(pl),
+ px_dst, 1, MV(px_depth), npix, rop)
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plglri.x b/sys/plio/tf/plglri.x
new file mode 100644
index 00000000..3f031ee5
--- /dev/null
+++ b/sys/plio/tf/plglri.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+include <plset.h>
+
+# PL_GLR -- Get a line segment as a range list, applying the given ROP to
+# combine the pixels with those of the output list.
+
+procedure pl_glri (pl, v, rl_dst, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+int rl_dst[ARB] #O output range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int mr, nr
+pointer sp, rl_out, rl_src, ll_src
+pointer pl_access()
+int pl_l2ri()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop))
+ nr = pl_l2ri (Mems[ll_src], v[1], rl_dst, npix)
+ else {
+ call smark (sp)
+ mr = min (RL_MAXLEN(pl), npix * 3)
+ call salloc (rl_src, mr, TY_INT)
+ call salloc (rl_out, mr, TY_INT)
+
+ nr = pl_l2ri (Mems[ll_src], v[1], Memi[rl_src], npix)
+ call pl_rangeropi (Memi[rl_src], 1, PL_MAXVAL(pl),
+ rl_dst, 1, MV(rl_depth),
+ Memi[rl_out], npix, rop)
+
+ # Copy out the edited range list.
+ call amovi (Memi[rl_out], rl_dst, RLI_LEN(rl_out))
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/tf/plglrl.x b/sys/plio/tf/plglrl.x
new file mode 100644
index 00000000..cf30dc62
--- /dev/null
+++ b/sys/plio/tf/plglrl.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+include <plset.h>
+
+# PL_GLR -- Get a line segment as a range list, applying the given ROP to
+# combine the pixels with those of the output list.
+
+procedure pl_glrl (pl, v, rl_dst, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+long rl_dst[ARB] #O output range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int mr, nr
+pointer sp, rl_out, rl_src, ll_src
+pointer pl_access()
+int pl_l2rl()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop))
+ nr = pl_l2rl (Mems[ll_src], v[1], rl_dst, npix)
+ else {
+ call smark (sp)
+ mr = min (RL_MAXLEN(pl), npix * 3)
+ call salloc (rl_src, mr, TY_LONG)
+ call salloc (rl_out, mr, TY_LONG)
+
+ nr = pl_l2rl (Mems[ll_src], v[1], Meml[rl_src], npix)
+ call pl_rangeropl (Meml[rl_src], 1, PL_MAXVAL(pl),
+ rl_dst, 1, MV(rl_depth),
+ Meml[rl_out], npix, rop)
+
+ # Copy out the edited range list.
+ call amovl (Meml[rl_out], rl_dst, RLL_LEN(rl_out))
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/tf/plglrs.x b/sys/plio/tf/plglrs.x
new file mode 100644
index 00000000..4c9c90a4
--- /dev/null
+++ b/sys/plio/tf/plglrs.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+include <plset.h>
+
+# PL_GLR -- Get a line segment as a range list, applying the given ROP to
+# combine the pixels with those of the output list.
+
+procedure pl_glrs (pl, v, rl_dst, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short rl_dst[ARB] #O output range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels desired
+int rop #I rasterop
+
+int mr, nr
+pointer sp, rl_out, rl_src, ll_src
+pointer pl_access()
+int pl_l2rs()
+errchk pl_access
+
+begin
+ ll_src = pl_access (pl,v)
+ if (!R_NEED_DST(rop))
+ nr = pl_l2rs (Mems[ll_src], v[1], rl_dst, npix)
+ else {
+ call smark (sp)
+ mr = min (RL_MAXLEN(pl), npix * 3)
+ call salloc (rl_src, mr, TY_SHORT)
+ call salloc (rl_out, mr, TY_SHORT)
+
+ nr = pl_l2rs (Mems[ll_src], v[1], Mems[rl_src], npix)
+ call pl_rangerops (Mems[rl_src], 1, PL_MAXVAL(pl),
+ rl_dst, 1, MV(rl_depth),
+ Mems[rl_out], npix, rop)
+
+ # Copy out the edited range list.
+ call amovs (Mems[rl_out], rl_dst, RLS_LEN(rl_out))
+
+ call sfree (sp)
+ }
+end
diff --git a/sys/plio/tf/pll2pi.x b/sys/plio/tf/pll2pi.x
new file mode 100644
index 00000000..cadea930
--- /dev/null
+++ b/sys/plio/tf/pll2pi.x
@@ -0,0 +1,105 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_L2P -- Convert a line list to a pixel array. The number of pixels output
+# (always npix) is returned as the function value.
+
+int procedure pl_l2pi (ll_src, xs, px_dst, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+int px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+int pv
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, ip, op, otop, i
+define putpix_ 91
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ skipword = false
+ op = 1
+ x1 = 1
+ pv = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN, I_HN, I_PN:
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+
+ # Process segment if any region is inbounds.
+ np = i2 - i1 + 1
+ if (np > 0) {
+ otop = op + np - 1
+ if (opcode == I_HN) {
+ do i = op, otop
+ px_dst[i] = pv
+ } else {
+ do i = op, otop
+ px_dst[i] = 0
+ if (opcode == I_PN && i2 == x2)
+ px_dst[otop] = pv
+ }
+ op = otop + 1
+ }
+
+ # Advance the line index.
+ x1 = x2 + 1
+
+ case I_SH:
+ pv = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ case I_IH:
+ pv = pv + data
+ case I_DH:
+ pv = pv - data
+ case I_IS:
+ pv = pv + data
+ goto putpix_
+ case I_DS:
+ pv = pv - data
+putpix_
+ if (x1 >= xs && x1 <= xe) {
+ px_dst[op] = pv
+ op = op + 1
+ }
+ x1 = x1 + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/pll2pl.x b/sys/plio/tf/pll2pl.x
new file mode 100644
index 00000000..9863019b
--- /dev/null
+++ b/sys/plio/tf/pll2pl.x
@@ -0,0 +1,105 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_L2P -- Convert a line list to a pixel array. The number of pixels output
+# (always npix) is returned as the function value.
+
+int procedure pl_l2pl (ll_src, xs, px_dst, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+long px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+long pv
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, ip, op, otop, i
+define putpix_ 91
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ skipword = false
+ op = 1
+ x1 = 1
+ pv = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN, I_HN, I_PN:
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+
+ # Process segment if any region is inbounds.
+ np = i2 - i1 + 1
+ if (np > 0) {
+ otop = op + np - 1
+ if (opcode == I_HN) {
+ do i = op, otop
+ px_dst[i] = pv
+ } else {
+ do i = op, otop
+ px_dst[i] = 0
+ if (opcode == I_PN && i2 == x2)
+ px_dst[otop] = pv
+ }
+ op = otop + 1
+ }
+
+ # Advance the line index.
+ x1 = x2 + 1
+
+ case I_SH:
+ pv = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ case I_IH:
+ pv = pv + data
+ case I_DH:
+ pv = pv - data
+ case I_IS:
+ pv = pv + data
+ goto putpix_
+ case I_DS:
+ pv = pv - data
+putpix_
+ if (x1 >= xs && x1 <= xe) {
+ px_dst[op] = pv
+ op = op + 1
+ }
+ x1 = x1 + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/pll2ps.x b/sys/plio/tf/pll2ps.x
new file mode 100644
index 00000000..a853e5ba
--- /dev/null
+++ b/sys/plio/tf/pll2ps.x
@@ -0,0 +1,105 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_L2P -- Convert a line list to a pixel array. The number of pixels output
+# (always npix) is returned as the function value.
+
+int procedure pl_l2ps (ll_src, xs, px_dst, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+short px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+short pv
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, ip, op, otop, i
+define putpix_ 91
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ skipword = false
+ op = 1
+ x1 = 1
+ pv = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN, I_HN, I_PN:
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+
+ # Process segment if any region is inbounds.
+ np = i2 - i1 + 1
+ if (np > 0) {
+ otop = op + np - 1
+ if (opcode == I_HN) {
+ do i = op, otop
+ px_dst[i] = pv
+ } else {
+ do i = op, otop
+ px_dst[i] = 0
+ if (opcode == I_PN && i2 == x2)
+ px_dst[otop] = pv
+ }
+ op = otop + 1
+ }
+
+ # Advance the line index.
+ x1 = x2 + 1
+
+ case I_SH:
+ pv = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ case I_IH:
+ pv = pv + data
+ case I_DH:
+ pv = pv - data
+ case I_IS:
+ pv = pv + data
+ goto putpix_
+ case I_DS:
+ pv = pv - data
+putpix_
+ if (x1 >= xs && x1 <= xe) {
+ px_dst[op] = pv
+ op = op + 1
+ }
+ x1 = x1 + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/pll2ri.x b/sys/plio/tf/pll2ri.x
new file mode 100644
index 00000000..431b97e2
--- /dev/null
+++ b/sys/plio/tf/pll2ri.x
@@ -0,0 +1,117 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_L2R -- Convert a line list to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_l2ri (ll_src, xs, rl, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+int rl[3,ARB] #O output range list
+int npix #I number of pixels to convert
+
+int pv, hi
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, rn, ip
+define range_ 91
+define putrange_ 92
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ rn = RL_FIRST
+ xe = xs + npix - 1
+ skipword = false
+ x1 = 1
+ hi = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ pv = 0
+ goto range_
+ case I_HN:
+ pv = hi
+range_
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+ np = i2 - i1 + 1
+ x1 = x2 + 1
+
+ case I_PN:
+ pv = hi
+ x2 = x1 + data - 1
+ if (x2 < xs || x2 > xe)
+ np = 0
+ else {
+ i1 = x2
+ np = 1
+ }
+ x1 = x2 + 1
+
+ case I_SH:
+ hi = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ next
+ case I_IH:
+ hi = hi + data
+ next
+ case I_DH:
+ hi = hi - data
+ next
+
+ case I_IS, I_DS:
+ if (opcode == I_IS)
+ hi = hi + data
+ else
+ hi = hi - data
+
+ i1 = max (x1, xs)
+ i2 = min (x1, xe)
+ np = i2 - i1 + 1
+ x1 = x1 + 1
+ pv = hi
+ }
+
+ # Output a range entry?
+ if (np > 0 && pv > 0) {
+ rl[1,rn] = i1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/pll2rl.x b/sys/plio/tf/pll2rl.x
new file mode 100644
index 00000000..0ff4ce4b
--- /dev/null
+++ b/sys/plio/tf/pll2rl.x
@@ -0,0 +1,117 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_L2R -- Convert a line list to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_l2rl (ll_src, xs, rl, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+long rl[3,ARB] #O output range list
+int npix #I number of pixels to convert
+
+int pv, hi
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, rn, ip
+define range_ 91
+define putrange_ 92
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ rn = RL_FIRST
+ xe = xs + npix - 1
+ skipword = false
+ x1 = 1
+ hi = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ pv = 0
+ goto range_
+ case I_HN:
+ pv = hi
+range_
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+ np = i2 - i1 + 1
+ x1 = x2 + 1
+
+ case I_PN:
+ pv = hi
+ x2 = x1 + data - 1
+ if (x2 < xs || x2 > xe)
+ np = 0
+ else {
+ i1 = x2
+ np = 1
+ }
+ x1 = x2 + 1
+
+ case I_SH:
+ hi = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ next
+ case I_IH:
+ hi = hi + data
+ next
+ case I_DH:
+ hi = hi - data
+ next
+
+ case I_IS, I_DS:
+ if (opcode == I_IS)
+ hi = hi + data
+ else
+ hi = hi - data
+
+ i1 = max (x1, xs)
+ i2 = min (x1, xe)
+ np = i2 - i1 + 1
+ x1 = x1 + 1
+ pv = hi
+ }
+
+ # Output a range entry?
+ if (np > 0 && pv > 0) {
+ rl[1,rn] = i1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/pll2rs.x b/sys/plio/tf/pll2rs.x
new file mode 100644
index 00000000..a0852774
--- /dev/null
+++ b/sys/plio/tf/pll2rs.x
@@ -0,0 +1,117 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_L2R -- Convert a line list to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_l2rs (ll_src, xs, rl, npix)
+
+short ll_src[ARB] #I input line list
+int xs #I starting index in ll_src
+short rl[3,ARB] #O output range list
+int npix #I number of pixels to convert
+
+int pv, hi
+bool skipword
+int opcode, data, ll_len, ll_first
+int x1, x2, i1, i2, xe, np, rn, ip
+define range_ 91
+define putrange_ 92
+
+begin
+ # Support old format line lists.
+ if (LL_OLDFORMAT(ll_src)) {
+ ll_len = OLL_LEN(ll_src)
+ ll_first = OLL_FIRST
+ } else {
+ ll_len = LL_LEN(ll_src)
+ ll_first = LL_FIRST(ll_src)
+ }
+
+ # No pixels?
+ if (npix <= 0 || ll_len <= 0)
+ return (0)
+
+ rn = RL_FIRST
+ xe = xs + npix - 1
+ skipword = false
+ x1 = 1
+ hi = 1
+
+ do ip = ll_first, ll_len {
+ if (skipword) {
+ skipword = false
+ next
+ }
+
+ opcode = I_OPCODE(ll_src[ip])
+ data = I_DATA(ll_src[ip])
+
+ switch (opcode) {
+ case I_ZN:
+ pv = 0
+ goto range_
+ case I_HN:
+ pv = hi
+range_
+ # Determine inbounds region of segment.
+ x2 = x1 + data - 1
+ i1 = max (x1, xs)
+ i2 = min (x2, xe)
+ np = i2 - i1 + 1
+ x1 = x2 + 1
+
+ case I_PN:
+ pv = hi
+ x2 = x1 + data - 1
+ if (x2 < xs || x2 > xe)
+ np = 0
+ else {
+ i1 = x2
+ np = 1
+ }
+ x1 = x2 + 1
+
+ case I_SH:
+ hi = (int(ll_src[ip+1]) * I_SHIFT) + data
+ skipword = true
+ next
+ case I_IH:
+ hi = hi + data
+ next
+ case I_DH:
+ hi = hi - data
+ next
+
+ case I_IS, I_DS:
+ if (opcode == I_IS)
+ hi = hi + data
+ else
+ hi = hi - data
+
+ i1 = max (x1, xs)
+ i2 = min (x1, xe)
+ np = i2 - i1 + 1
+ x1 = x1 + 1
+ pv = hi
+ }
+
+ # Output a range entry?
+ if (np > 0 && pv > 0) {
+ rl[1,rn] = i1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ if (x1 > xe)
+ break
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/plp2li.x b/sys/plio/tf/plp2li.x
new file mode 100644
index 00000000..58b3a8b5
--- /dev/null
+++ b/sys/plio/tf/plp2li.x
@@ -0,0 +1,126 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_P2L -- Convert a pixel array to a line list. The length of the list is
+# returned as the function value.
+
+int procedure pl_p2li (px_src, xs, ll_dst, npix)
+
+int px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+int hi, pv, nv, zero
+int xe, x1, iz, ip, op, np, nz, dv, v
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+
+ # Pack the pixel array into a line list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ iz = xs # start index of range of zeros
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ } else if (pv == 0)
+ x1 = xe + 1
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ np = ip - x1 + 1
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ # The I_DATAMAX-1 limit is to allow adding M_PN+1 without
+ # overflowing the range of the data segment.
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ x1 = ip + 1
+ iz = x1
+ pv = nv
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plp2ll.x b/sys/plio/tf/plp2ll.x
new file mode 100644
index 00000000..fa4a7d13
--- /dev/null
+++ b/sys/plio/tf/plp2ll.x
@@ -0,0 +1,126 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_P2L -- Convert a pixel array to a line list. The length of the list is
+# returned as the function value.
+
+int procedure pl_p2ll (px_src, xs, ll_dst, npix)
+
+long px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+long hi, pv, nv, zero
+int xe, x1, iz, ip, op, np, nz, dv, v
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+
+ # Pack the pixel array into a line list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ iz = xs # start index of range of zeros
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ } else if (pv == 0)
+ x1 = xe + 1
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ np = ip - x1 + 1
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ # The I_DATAMAX-1 limit is to allow adding M_PN+1 without
+ # overflowing the range of the data segment.
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ x1 = ip + 1
+ iz = x1
+ pv = nv
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plp2ls.x b/sys/plio/tf/plp2ls.x
new file mode 100644
index 00000000..327778dc
--- /dev/null
+++ b/sys/plio/tf/plp2ls.x
@@ -0,0 +1,126 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_P2L -- Convert a pixel array to a line list. The length of the list is
+# returned as the function value.
+
+int procedure pl_p2ls (px_src, xs, ll_dst, npix)
+
+short px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+short hi, pv, nv, zero
+int xe, x1, iz, ip, op, np, nz, dv, v
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+
+ # Pack the pixel array into a line list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ iz = xs # start index of range of zeros
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ } else if (pv == 0)
+ x1 = xe + 1
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ np = ip - x1 + 1
+ nz = x1 - iz
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ # The I_DATAMAX-1 limit is to allow adding M_PN+1 without
+ # overflowing the range of the data segment.
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ x1 = ip + 1
+ iz = x1
+ pv = nv
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plp2ri.x b/sys/plio/tf/plp2ri.x
new file mode 100644
index 00000000..64f4f1eb
--- /dev/null
+++ b/sys/plio/tf/plp2ri.x
@@ -0,0 +1,71 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_P2R -- Convert a pixel array to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_p2ri (px_src, xs, rl, npix)
+
+int px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+int rl[3,ARB] #O destination range list
+int npix #I number of pixels to convert
+
+int hi, pv, zero
+int xe, x1, np, rn, nv, ip
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ rn = RL_FIRST
+
+ # Pack the pixel array into a range list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ }
+
+ np = ip - x1 + 1
+
+ # Output the new range.
+ if (pv > 0) {
+ rl[1,rn] = x1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ x1 = ip + 1
+ pv = nv
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/plp2rl.x b/sys/plio/tf/plp2rl.x
new file mode 100644
index 00000000..574faad9
--- /dev/null
+++ b/sys/plio/tf/plp2rl.x
@@ -0,0 +1,71 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_P2R -- Convert a pixel array to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_p2rl (px_src, xs, rl, npix)
+
+long px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+long rl[3,ARB] #O destination range list
+int npix #I number of pixels to convert
+
+long hi, pv, zero
+int xe, x1, np, rn, nv, ip
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ rn = RL_FIRST
+
+ # Pack the pixel array into a range list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ }
+
+ np = ip - x1 + 1
+
+ # Output the new range.
+ if (pv > 0) {
+ rl[1,rn] = x1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ x1 = ip + 1
+ pv = nv
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/plp2rs.x b/sys/plio/tf/plp2rs.x
new file mode 100644
index 00000000..da082f47
--- /dev/null
+++ b/sys/plio/tf/plp2rs.x
@@ -0,0 +1,71 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_P2R -- Convert a pixel array to a range list. The length of the output
+# range list is returned as the function value.
+
+int procedure pl_p2rs (px_src, xs, rl, npix)
+
+short px_src[ARB] #I input pixel array
+int xs #I starting index in pixbuf
+short rl[3,ARB] #O destination range list
+int npix #I number of pixels to convert
+
+short hi, pv, zero
+int xe, x1, np, rn, nv, ip
+define done_ 91
+
+begin
+ # No input pixels?
+ if (npix <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ rn = RL_FIRST
+
+ # Pack the pixel array into a range list. This is done by scanning
+ # the pixel list for successive ranges of pixels of constant nonzero
+ # value, where each range is described as follows:
+
+ zero = 0
+ pv = max (zero, px_src[xs]) # pixel value of current range
+ x1 = xs # start index of current range
+ hi = 1 # current high value
+
+ # Process the data array.
+ do ip = xs, xe {
+ if (ip < xe) {
+ # Get the next pixel value, loop again if same as previous one.
+ nv = max (zero, px_src[ip+1])
+ if (nv == pv)
+ next
+
+ # If current range is zero, loop again to get nonzero range.
+ if (pv == 0) {
+ pv = nv
+ x1 = ip + 1
+ next
+ }
+ }
+
+ np = ip - x1 + 1
+
+ # Output the new range.
+ if (pv > 0) {
+ rl[1,rn] = x1
+ rl[2,rn] = np
+ rl[3,rn] = pv
+ rn = rn + 1
+ }
+
+ x1 = ip + 1
+ pv = nv
+ }
+
+ RL_LEN(rl) = rn - 1
+ RL_AXLEN(rl) = npix
+
+ return (rn - 1)
+end
diff --git a/sys/plio/tf/plplpi.x b/sys/plio/tf/plplpi.x
new file mode 100644
index 00000000..26c094d4
--- /dev/null
+++ b/sys/plio/tf/plplpi.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLP -- Put a line segment input as a pixel array to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plpi (pl, v, px_src, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+int px_src[ARB] #I input pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_p2li()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the pixel array to a line list.
+ ll_len = pl_p2li (px_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(px_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plplpl.x b/sys/plio/tf/plplpl.x
new file mode 100644
index 00000000..85a11304
--- /dev/null
+++ b/sys/plio/tf/plplpl.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLP -- Put a line segment input as a pixel array to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plpl (pl, v, px_src, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+long px_src[ARB] #I input pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_p2ll()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the pixel array to a line list.
+ ll_len = pl_p2ll (px_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(px_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plplps.x b/sys/plio/tf/plplps.x
new file mode 100644
index 00000000..b693c90e
--- /dev/null
+++ b/sys/plio/tf/plplps.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLP -- Put a line segment input as a pixel array to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plps (pl, v, px_src, px_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short px_src[ARB] #I input pixel array
+int px_depth #I pixel depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_p2ls()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the pixel array to a line list.
+ ll_len = pl_p2ls (px_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(px_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plplri.x b/sys/plio/tf/plplri.x
new file mode 100644
index 00000000..433f9a88
--- /dev/null
+++ b/sys/plio/tf/plplri.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLR -- Put a line segment input as a range list to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plri (pl, v, rl_src, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+int rl_src[ARB] #I input range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_r2li()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the range list to a line list.
+ ll_len = pl_r2li (rl_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(rl_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plplrl.x b/sys/plio/tf/plplrl.x
new file mode 100644
index 00000000..bf40b9ef
--- /dev/null
+++ b/sys/plio/tf/plplrl.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLR -- Put a line segment input as a range list to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plrl (pl, v, rl_src, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+long rl_src[ARB] #I input range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_r2ll()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the range list to a line list.
+ ll_len = pl_r2ll (rl_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(rl_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plplrs.x b/sys/plio/tf/plplrs.x
new file mode 100644
index 00000000..13318070
--- /dev/null
+++ b/sys/plio/tf/plplrs.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_PLR -- Put a line segment input as a range list to a mask, applying the
+# given ROP to combine the line segment with the existing line of the mask.
+
+procedure pl_plrs (pl, v, rl_src, rl_depth, npix, rop)
+
+pointer pl #I mask descriptor
+long v[PL_MAXDIM] #I vector coords of line segment
+short rl_src[ARB] #I input range list
+int rl_depth #I range list depth, bits
+int npix #I number of pixels to be set
+int rop #I rasterop
+
+int ll_len
+pointer sp, ll_src, ll_out, ll_dst
+pointer pl_access()
+int pl_r2ls()
+errchk pl_access
+
+begin
+ call smark (sp)
+ call salloc (ll_src, LL_MAXLEN(pl), TY_SHORT)
+
+ # Convert the range list to a line list.
+ ll_len = pl_r2ls (rl_src, 1, Mems[ll_src], npix)
+
+ if (!R_NEED_DST(rop) && v[1] == 1 && npix == PL_AXLEN(pl,1))
+ call pl_update (pl, v, Mems[ll_src])
+ else {
+ call salloc (ll_out, LL_MAXLEN(pl), TY_SHORT)
+ ll_dst = pl_access (pl,v)
+ call pl_linerop (Mems[ll_src], 1, PL_MAXVAL(pl), Mems[ll_dst], v[1],
+ MV(rl_depth), Mems[ll_out], npix, rop)
+ call pl_update (pl, v, Mems[ll_out])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plpropi.x b/sys/plio/tf/plpropi.x
new file mode 100644
index 00000000..ecfe7227
--- /dev/null
+++ b/sys/plio/tf/plpropi.x
@@ -0,0 +1,177 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_PIXROP -- Rasterop between source and destination pixel arrays.
+
+procedure pl_pixropi (px_src,xs,src_maxval, px_dst,ds,dst_maxval, npix, rop)
+
+int px_src[ARB] #I source pixel array
+int xs #I starting pixel index in src
+int src_maxval #I max pixel value in src mask
+int px_dst[ARB] #O destination pixel array
+int ds #I starting pixel index in dst
+int dst_maxval #I max pixel value in dst mask
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+pointer sp, src
+int opcode, i
+int data, ceil, src_value
+int and(), or(), xor(), not()
+define out_ 91
+
+begin
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+ ceil = 0
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Handle the easy cases first.
+ switch (opcode) {
+ case PIX_CLR:
+ call aclri (px_dst[ds], npix)
+ return
+ case PIX_SET:
+ call amovki (data, px_dst[ds], npix)
+ goto out_
+ case PIX_SRC:
+ if (src_maxval != 1)
+ call amovi (px_src[xs], px_dst[ds], npix)
+ else {
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ px_dst[ds+i-1] = src_value
+ else
+ px_dst[ds+i-1] = 0
+ }
+
+ goto out_
+ case PIX_DST:
+ return # no-op
+ }
+
+ # Integer or boolean source mask?
+ if (src_maxval != 1) {
+ # Integer source mask; operate directly on source mask.
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_src[xs+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (px_src[xs+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(px_src[xs+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(px_src[xs+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (px_src[xs+i-1], px_dst[ds+i-1]))
+ }
+
+ } else {
+ # Boolean source mask; use integer DATA value from ROP if source
+ # mask pixel is set.
+
+ call smark (sp)
+ call salloc (src, npix, TY_INT)
+
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ Memi[src+i-1] = src_value
+ else
+ Memi[src+i-1] = 0
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (Memi[src+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Memi[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Memi[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (Memi[src+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Memi[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Memi[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(Memi[src+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(Memi[src+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (Memi[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (Memi[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (Memi[src+i-1], px_dst[ds+i-1]))
+ }
+
+ call sfree (sp)
+ }
+out_
+ # If writing to an integer mask, mask the data to the indicated max
+ # value (necessary to avoid very large values if any NOT operations
+ # occurred). If writing to a boolean mask, map positive integer mask
+ # values to 1.
+
+ if (dst_maxval == 1) {
+ data = 1
+ call argti (px_dst[ds], npix, ceil, data)
+ } else if (dst_maxval > 1) {
+ data = dst_maxval
+ call aandki (px_dst[ds], data, px_dst[ds], npix)
+ }
+end
diff --git a/sys/plio/tf/plpropl.x b/sys/plio/tf/plpropl.x
new file mode 100644
index 00000000..1aa3b21c
--- /dev/null
+++ b/sys/plio/tf/plpropl.x
@@ -0,0 +1,177 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_PIXROP -- Rasterop between source and destination pixel arrays.
+
+procedure pl_pixropl (px_src,xs,src_maxval, px_dst,ds,dst_maxval, npix, rop)
+
+long px_src[ARB] #I source pixel array
+int xs #I starting pixel index in src
+int src_maxval #I max pixel value in src mask
+long px_dst[ARB] #O destination pixel array
+int ds #I starting pixel index in dst
+int dst_maxval #I max pixel value in dst mask
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+pointer sp, src
+int opcode, i
+long data, ceil, src_value
+int and(), or(), xor(), not()
+define out_ 91
+
+begin
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+ ceil = 0
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Handle the easy cases first.
+ switch (opcode) {
+ case PIX_CLR:
+ call aclrl (px_dst[ds], npix)
+ return
+ case PIX_SET:
+ call amovkl (data, px_dst[ds], npix)
+ goto out_
+ case PIX_SRC:
+ if (src_maxval != 1)
+ call amovl (px_src[xs], px_dst[ds], npix)
+ else {
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ px_dst[ds+i-1] = src_value
+ else
+ px_dst[ds+i-1] = 0
+ }
+
+ goto out_
+ case PIX_DST:
+ return # no-op
+ }
+
+ # Integer or boolean source mask?
+ if (src_maxval != 1) {
+ # Integer source mask; operate directly on source mask.
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_src[xs+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (px_src[xs+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(px_src[xs+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(px_src[xs+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (px_src[xs+i-1], px_dst[ds+i-1]))
+ }
+
+ } else {
+ # Boolean source mask; use integer DATA value from ROP if source
+ # mask pixel is set.
+
+ call smark (sp)
+ call salloc (src, npix, TY_LONG)
+
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ Meml[src+i-1] = src_value
+ else
+ Meml[src+i-1] = 0
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (Meml[src+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Meml[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Meml[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (Meml[src+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Meml[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Meml[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(Meml[src+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(Meml[src+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (Meml[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (Meml[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (Meml[src+i-1], px_dst[ds+i-1]))
+ }
+
+ call sfree (sp)
+ }
+out_
+ # If writing to an integer mask, mask the data to the indicated max
+ # value (necessary to avoid very large values if any NOT operations
+ # occurred). If writing to a boolean mask, map positive integer mask
+ # values to 1.
+
+ if (dst_maxval == 1) {
+ data = 1
+ call argtl (px_dst[ds], npix, ceil, data)
+ } else if (dst_maxval > 1) {
+ data = dst_maxval
+ call aandkl (px_dst[ds], data, px_dst[ds], npix)
+ }
+end
diff --git a/sys/plio/tf/plprops.x b/sys/plio/tf/plprops.x
new file mode 100644
index 00000000..f06baadb
--- /dev/null
+++ b/sys/plio/tf/plprops.x
@@ -0,0 +1,177 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_PIXROP -- Rasterop between source and destination pixel arrays.
+
+procedure pl_pixrops (px_src,xs,src_maxval, px_dst,ds,dst_maxval, npix, rop)
+
+short px_src[ARB] #I source pixel array
+int xs #I starting pixel index in src
+int src_maxval #I max pixel value in src mask
+short px_dst[ARB] #O destination pixel array
+int ds #I starting pixel index in dst
+int dst_maxval #I max pixel value in dst mask
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+pointer sp, src
+int opcode, i
+short data, ceil, src_value
+int and(), or(), xor(), not()
+define out_ 91
+
+begin
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+ ceil = 0
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Handle the easy cases first.
+ switch (opcode) {
+ case PIX_CLR:
+ call aclrs (px_dst[ds], npix)
+ return
+ case PIX_SET:
+ call amovks (data, px_dst[ds], npix)
+ goto out_
+ case PIX_SRC:
+ if (src_maxval != 1)
+ call amovs (px_src[xs], px_dst[ds], npix)
+ else {
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ px_dst[ds+i-1] = src_value
+ else
+ px_dst[ds+i-1] = 0
+ }
+
+ goto out_
+ case PIX_DST:
+ return # no-op
+ }
+
+ # Integer or boolean source mask?
+ if (src_maxval != 1) {
+ # Integer source mask; operate directly on source mask.
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_src[xs+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (px_src[xs+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (px_src[xs+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(px_src[xs+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(px_src[xs+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (px_src[xs+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (px_src[xs+i-1], px_dst[ds+i-1]))
+ }
+
+ } else {
+ # Boolean source mask; use integer DATA value from ROP if source
+ # mask pixel is set.
+
+ call smark (sp)
+ call salloc (src, npix, TY_SHORT)
+
+ do i = 1, npix
+ if (px_src[xs+i-1] > 0)
+ Mems[src+i-1] = src_value
+ else
+ Mems[src+i-1] = 0
+
+ switch (opcode) {
+ case PIX_NOTSRC:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (Mems[src+i-1])
+ case PIX_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (px_dst[xs+i-1])
+
+ case PIX_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Mems[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Mems[src+i-1], px_dst[ds+i-1])
+ case PIX_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = xor (Mems[src+i-1], px_dst[ds+i-1])
+
+ case PIX_SRC_AND_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (Mems[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_SRC_OR_NOTDST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (Mems[src+i-1], not(px_dst[ds+i-1]))
+ case PIX_NOTSRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = and (not(Mems[src+i-1]), px_dst[ds+i-1])
+ case PIX_NOTSRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = or (not(Mems[src+i-1]), px_dst[ds+i-1])
+
+ case PIX_NOT_SRC_AND_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (and (Mems[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_OR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not ( or (Mems[src+i-1], px_dst[ds+i-1]))
+ case PIX_NOT_SRC_XOR_DST:
+ do i = 1, npix
+ px_dst[ds+i-1] = not (xor (Mems[src+i-1], px_dst[ds+i-1]))
+ }
+
+ call sfree (sp)
+ }
+out_
+ # If writing to an integer mask, mask the data to the indicated max
+ # value (necessary to avoid very large values if any NOT operations
+ # occurred). If writing to a boolean mask, map positive integer mask
+ # values to 1.
+
+ if (dst_maxval == 1) {
+ data = 1
+ call argts (px_dst[ds], npix, ceil, data)
+ } else if (dst_maxval > 1) {
+ data = dst_maxval
+ call aandks (px_dst[ds], data, px_dst[ds], npix)
+ }
+end
diff --git a/sys/plio/tf/plr2li.x b/sys/plio/tf/plr2li.x
new file mode 100644
index 00000000..f6fb6c79
--- /dev/null
+++ b/sys/plio/tf/plr2li.x
@@ -0,0 +1,130 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2L -- Convert a range list to a line list. The length of the output
+# line list is returned as the function value.
+
+int procedure pl_r2li (rl_src, xs, ll_dst, npix)
+
+int rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+int hi, pv
+int last, xe, x1, x2, iz, op, np, nz, nr, dv, v, i
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+ iz = xs
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr + 1 {
+ if (i <= nr) {
+ # Load next range.
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+ last = x2
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ # Go again if nothing inbounds.
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ } else if (iz < xe) {
+ # At end of input range list, but need to output a ZN.
+ nz = xe - iz + 1
+ np = 0
+ pv = 0
+ } else
+ break
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0 && x2 == last) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ iz = x2 + 1
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plr2ll.x b/sys/plio/tf/plr2ll.x
new file mode 100644
index 00000000..b21d6678
--- /dev/null
+++ b/sys/plio/tf/plr2ll.x
@@ -0,0 +1,130 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2L -- Convert a range list to a line list. The length of the output
+# line list is returned as the function value.
+
+int procedure pl_r2ll (rl_src, xs, ll_dst, npix)
+
+long rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+long hi, pv
+int last, xe, x1, x2, iz, op, np, nz, nr, dv, v, i
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+ iz = xs
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr + 1 {
+ if (i <= nr) {
+ # Load next range.
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+ last = x2
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ # Go again if nothing inbounds.
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ } else if (iz < xe) {
+ # At end of input range list, but need to output a ZN.
+ nz = xe - iz + 1
+ np = 0
+ pv = 0
+ } else
+ break
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0 && x2 == last) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ iz = x2 + 1
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plr2ls.x b/sys/plio/tf/plr2ls.x
new file mode 100644
index 00000000..6aceca17
--- /dev/null
+++ b/sys/plio/tf/plr2ls.x
@@ -0,0 +1,130 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2L -- Convert a range list to a line list. The length of the output
+# line list is returned as the function value.
+
+int procedure pl_r2ls (rl_src, xs, ll_dst, npix)
+
+short rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+short ll_dst[ARB] #O destination line list
+int npix #I number of pixels to convert
+
+short hi, pv
+int last, xe, x1, x2, iz, op, np, nz, nr, dv, v, i
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ # Initialize the linelist header.
+ LL_VERSION(ll_dst) = LL_CURVERSION
+ LL_HDRLEN(ll_dst) = LL_CURHDRLEN
+ LL_NREFS(ll_dst) = 0
+ LL_SETBLEN(ll_dst,0)
+
+ xe = xs + npix - 1
+ op = LL_CURHDRLEN + 1
+ iz = xs
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr + 1 {
+ if (i <= nr) {
+ # Load next range.
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+ last = x2
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ # Go again if nothing inbounds.
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ } else if (iz < xe) {
+ # At end of input range list, but need to output a ZN.
+ nz = xe - iz + 1
+ np = 0
+ pv = 0
+ } else
+ break
+
+ # Encode an instruction to regenerate the current range I0-IP
+ # of N data values of nonzero level PV. In the most complex case
+ # we must update the high value and output a range of zeros,
+ # followed by a range of NP high values. If NP is 1, we can
+ # probably use a PN or [ID]S instruction to save space.
+
+ # Change the high value?
+ if (pv > 0) {
+ dv = pv - hi
+ if (dv != 0) {
+ # Output IH or DH instruction?
+ hi = pv
+ if (abs(dv) > I_DATAMAX) {
+ ll_dst[op] = M_SH + and (int(pv), I_DATAMAX)
+ op = op + 1
+ ll_dst[op] = pv / I_SHIFT
+ op = op + 1
+ } else {
+ if (dv < 0)
+ ll_dst[op] = M_DH + (-dv)
+ else
+ ll_dst[op] = M_IH + dv
+ op = op + 1
+
+ # Convert to IS or DS if range is a single pixel.
+ if (np == 1 && nz == 0) {
+ v = ll_dst[op-1]
+ ll_dst[op-1] = or (v, M_MOVE)
+ goto done_
+ }
+ }
+ }
+ }
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ # Output the ZN instruction.
+ for (; nz > 0; nz = nz - (I_DATAMAX-1)) {
+ ll_dst[op] = M_ZN + min(I_DATAMAX-1,nz)
+ op = op + 1
+ }
+ # Convert to PN if range is a single pixel.
+ if (np == 1 && pv > 0 && x2 == last) {
+ ll_dst[op-1] = ll_dst[op-1] + M_PN + 1
+ goto done_
+ }
+ }
+
+ # The only thing left is the HN instruction if we get here.
+ for (; np > 0; np = np - I_DATAMAX) {
+ ll_dst[op] = M_HN + min(I_DATAMAX,np)
+ op = op + 1
+ }
+done_
+ iz = x2 + 1
+ }
+
+ LL_SETLEN(ll_dst, op - 1)
+ return (op - 1)
+end
diff --git a/sys/plio/tf/plr2pi.x b/sys/plio/tf/plr2pi.x
new file mode 100644
index 00000000..57db5b63
--- /dev/null
+++ b/sys/plio/tf/plr2pi.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2P -- Convert a range list to a pixel array. The number of pixels
+# output (always npix) is returned as the function value.
+
+int procedure pl_r2pi (rl_src, xs, px_dst, npix)
+
+int rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+int px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+int hi, pv
+int xe, x1, x2, iz, op, np, nz, nr, i, j
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ iz = xs
+ op = 1
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr {
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ do j = 1, nz
+ px_dst[op+j-1] = 0
+ op = op + nz
+ }
+
+ # Output the pixels.
+ do j = 1, np
+ px_dst[op+j-1] = pv
+ op = op + np
+done_
+ x1 = x2 + 1
+ iz = x1
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/plr2pl.x b/sys/plio/tf/plr2pl.x
new file mode 100644
index 00000000..763bb8d2
--- /dev/null
+++ b/sys/plio/tf/plr2pl.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2P -- Convert a range list to a pixel array. The number of pixels
+# output (always npix) is returned as the function value.
+
+int procedure pl_r2pl (rl_src, xs, px_dst, npix)
+
+long rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+long px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+long hi, pv
+int xe, x1, x2, iz, op, np, nz, nr, i, j
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ iz = xs
+ op = 1
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr {
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ do j = 1, nz
+ px_dst[op+j-1] = 0
+ op = op + nz
+ }
+
+ # Output the pixels.
+ do j = 1, np
+ px_dst[op+j-1] = pv
+ op = op + np
+done_
+ x1 = x2 + 1
+ iz = x1
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/plr2ps.x b/sys/plio/tf/plr2ps.x
new file mode 100644
index 00000000..ecba387d
--- /dev/null
+++ b/sys/plio/tf/plr2ps.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PL_R2P -- Convert a range list to a pixel array. The number of pixels
+# output (always npix) is returned as the function value.
+
+int procedure pl_r2ps (rl_src, xs, px_dst, npix)
+
+short rl_src[3,ARB] #I input range list
+int xs #I starting pixel index in range list
+short px_dst[ARB] #O output pixel array
+int npix #I number of pixels to convert
+
+short hi, pv
+int xe, x1, x2, iz, op, np, nz, nr, i, j
+define done_ 91
+
+begin
+ # No input pixels?
+ nr = RL_LEN(rl_src)
+ if (npix <= 0 || nr <= 0)
+ return (0)
+
+ xe = xs + npix - 1
+ iz = xs
+ op = 1
+ hi = 1
+
+ # Process the array of range lists.
+ do i = RL_FIRST, nr {
+ x1 = rl_src[1,i]
+ np = rl_src[2,i]
+ pv = rl_src[3,i]
+ x2 = x1 + np - 1
+
+ # Get an inbounds range.
+ if (x1 > xe)
+ break
+ else if (xs > x2)
+ next
+ else if (x1 < xs)
+ x1 = xs
+ else if (x2 > xe)
+ x2 = xe
+
+ nz = x1 - iz
+ np = x2 - x1 + 1
+ if (np <= 0)
+ next
+
+ # Output range of zeros to catch up to current range?
+ if (nz > 0) {
+ do j = 1, nz
+ px_dst[op+j-1] = 0
+ op = op + nz
+ }
+
+ # Output the pixels.
+ do j = 1, np
+ px_dst[op+j-1] = pv
+ op = op + np
+done_
+ x1 = x2 + 1
+ iz = x1
+ }
+
+ # Zero any remaining output range.
+ do i = op, npix
+ px_dst[i] = 0
+
+ return (npix)
+end
diff --git a/sys/plio/tf/plreqi.x b/sys/plio/tf/plreqi.x
new file mode 100644
index 00000000..0307efea
--- /dev/null
+++ b/sys/plio/tf/plreqi.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+
+# PLR_EQUAL -- Compare two range lists for equality.
+
+bool procedure plr_equali (r1, r2)
+
+int r1[3,ARB] #I range list 1
+int r2[3,ARB] #I range list 2
+
+int i
+int len1, len2
+
+begin
+ len1 = RL_LEN(r1)
+ len2 = RL_LEN(r2)
+
+ if (len1 != len2)
+ return (false)
+
+ do i = RL_FIRST, len1
+ if (r1[1,i] != r2[1,i] || r1[2,i] != r2[2,i] || r1[3,i] != r2[3,i])
+ return (false)
+
+ return (true)
+end
diff --git a/sys/plio/tf/plreql.x b/sys/plio/tf/plreql.x
new file mode 100644
index 00000000..1fb76d6a
--- /dev/null
+++ b/sys/plio/tf/plreql.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+
+# PLR_EQUAL -- Compare two range lists for equality.
+
+bool procedure plr_equall (r1, r2)
+
+long r1[3,ARB] #I range list 1
+long r2[3,ARB] #I range list 2
+
+int i
+int len1, len2
+
+begin
+ len1 = RL_LEN(r1)
+ len2 = RL_LEN(r2)
+
+ if (len1 != len2)
+ return (false)
+
+ do i = RL_FIRST, len1
+ if (r1[1,i] != r2[1,i] || r1[2,i] != r2[2,i] || r1[3,i] != r2[3,i])
+ return (false)
+
+ return (true)
+end
diff --git a/sys/plio/tf/plreqs.x b/sys/plio/tf/plreqs.x
new file mode 100644
index 00000000..b44bf4da
--- /dev/null
+++ b/sys/plio/tf/plreqs.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+
+# PLR_EQUAL -- Compare two range lists for equality.
+
+bool procedure plr_equals (r1, r2)
+
+short r1[3,ARB] #I range list 1
+short r2[3,ARB] #I range list 2
+
+int i
+int len1, len2
+
+begin
+ len1 = RL_LEN(r1)
+ len2 = RL_LEN(r2)
+
+ if (len1 != len2)
+ return (false)
+
+ do i = RL_FIRST, len1
+ if (r1[1,i] != r2[1,i] || r1[2,i] != r2[2,i] || r1[3,i] != r2[3,i])
+ return (false)
+
+ return (true)
+end
diff --git a/sys/plio/tf/plrpri.x b/sys/plio/tf/plrpri.x
new file mode 100644
index 00000000..a74e7791
--- /dev/null
+++ b/sys/plio/tf/plrpri.x
@@ -0,0 +1,56 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PLR_PRINT -- Print a range list on the given output stream.
+
+procedure plr_printi (rl, fd, label, firstcol, maxcol)
+
+int rl[3,ARB] #I range list
+int fd #I output file
+char label[ARB] #I line label
+int firstcol #I first column for output
+int maxcol #I width of formatted output
+
+pointer sp, buf
+int col, rn, r_len, x, n, pv
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ # Output the line label and advance to the first column. If the label
+ # extends beyond the first column, start a new line.
+
+ call putline (fd, label)
+ col = strlen (label) + 1
+ if (col > firstcol)
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+
+ r_len = RL_LEN(rl)
+
+ # Decode the range list proper.
+ do rn = RL_FIRST, r_len {
+ x = RL_X(rl,rn)
+ n = RL_N(rl,rn)
+ pv = RL_V(rl,rn)
+
+ if (n == 1) {
+ call sprintf (Memc[buf], SZ_LINE, "%d(%d)")
+ call pargi (x)
+ call pargi (pv)
+ } else {
+ call sprintf (Memc[buf], SZ_LINE, "%d-%d(%d)")
+ call pargi (x)
+ call pargi (x+n-1)
+ call pargi (pv)
+ }
+
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ }
+
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plrprl.x b/sys/plio/tf/plrprl.x
new file mode 100644
index 00000000..6357cd71
--- /dev/null
+++ b/sys/plio/tf/plrprl.x
@@ -0,0 +1,56 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PLR_PRINT -- Print a range list on the given output stream.
+
+procedure plr_printl (rl, fd, label, firstcol, maxcol)
+
+long rl[3,ARB] #I range list
+int fd #I output file
+char label[ARB] #I line label
+int firstcol #I first column for output
+int maxcol #I width of formatted output
+
+pointer sp, buf
+int col, rn, r_len, x, n, pv
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ # Output the line label and advance to the first column. If the label
+ # extends beyond the first column, start a new line.
+
+ call putline (fd, label)
+ col = strlen (label) + 1
+ if (col > firstcol)
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+
+ r_len = RL_LEN(rl)
+
+ # Decode the range list proper.
+ do rn = RL_FIRST, r_len {
+ x = RL_X(rl,rn)
+ n = RL_N(rl,rn)
+ pv = RL_V(rl,rn)
+
+ if (n == 1) {
+ call sprintf (Memc[buf], SZ_LINE, "%d(%d)")
+ call pargi (x)
+ call pargi (pv)
+ } else {
+ call sprintf (Memc[buf], SZ_LINE, "%d-%d(%d)")
+ call pargi (x)
+ call pargi (x+n-1)
+ call pargi (pv)
+ }
+
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ }
+
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plrprs.x b/sys/plio/tf/plrprs.x
new file mode 100644
index 00000000..e055f851
--- /dev/null
+++ b/sys/plio/tf/plrprs.x
@@ -0,0 +1,56 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+
+# PLR_PRINT -- Print a range list on the given output stream.
+
+procedure plr_prints (rl, fd, label, firstcol, maxcol)
+
+short rl[3,ARB] #I range list
+int fd #I output file
+char label[ARB] #I line label
+int firstcol #I first column for output
+int maxcol #I width of formatted output
+
+pointer sp, buf
+int col, rn, r_len, x, n, pv
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ # Output the line label and advance to the first column. If the label
+ # extends beyond the first column, start a new line.
+
+ call putline (fd, label)
+ col = strlen (label) + 1
+ if (col > firstcol)
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+
+ r_len = RL_LEN(rl)
+
+ # Decode the range list proper.
+ do rn = RL_FIRST, r_len {
+ x = RL_X(rl,rn)
+ n = RL_N(rl,rn)
+ pv = RL_V(rl,rn)
+
+ if (n == 1) {
+ call sprintf (Memc[buf], SZ_LINE, "%d(%d)")
+ call pargi (x)
+ call pargi (pv)
+ } else {
+ call sprintf (Memc[buf], SZ_LINE, "%d-%d(%d)")
+ call pargi (x)
+ call pargi (x+n-1)
+ call pargi (pv)
+ }
+
+ call pl_debugout (fd, Memc[buf], col, firstcol, maxcol)
+ }
+
+ call pl_debugout (fd, "", col, firstcol, maxcol)
+ call sfree (sp)
+end
diff --git a/sys/plio/tf/plrropi.x b/sys/plio/tf/plrropi.x
new file mode 100644
index 00000000..72292323
--- /dev/null
+++ b/sys/plio/tf/plrropi.x
@@ -0,0 +1,195 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "../plrseg.h"
+
+# PL_RANGEROP -- Rasterop operation between source and destination range lists.
+# The indicated rasterop operation is performed upon the source and destination
+# range lists, writing the result to RL_OUT, which is a copy of RL_DST except
+# for the region affected by the rasterop operation (note that the destination
+# range list cannot be edited in place since it may change size).
+
+procedure pl_rangeropi (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+
+int rl_src[3,ARB] #I source range list
+int xs #I starting pixel index in src range list
+int src_maxval #I max pixel value in src mask
+int rl_dst[3,ARB] #I destination range list
+int ds #I starting pixel index in dst range list
+int dst_maxval #I max pixel value in dst mask
+int rl_out[3,ARB] #O output list (edited version of rl_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+bool need_src, need_dst, rop_enable
+int data, src_value, v_src, v_dst, pv
+int segsize, opcode, x, i, np, rn_o, p
+int d_src[LEN_PLRDES], d_dst[LEN_PLRDES]
+
+begin
+ need_src = R_NEED_SRC(rop)
+ need_dst = R_NEED_DST(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Advance to the desired position in the source list, discarding
+ # the unused ranges. The point XS may lie within a range or in a
+ # zero area of the input line.
+
+ if (need_src) {
+ x = 1
+ plr_init (rl_src, d_src)
+ do i = 1, ARB {
+ np = min (plr_nleft(d_src), xs - x)
+ plr_getseg (rl_src, d_src, np, v_src)
+ x = x + np
+ if (x >= xs || np == 0)
+ break
+ }
+ }
+
+ # Advance through both the source and destination lists, extracting
+ # line segments which have a constant value in each input list; the
+ # values for the two lists may differ. Apply the given rasterop to
+ # the source and destination pixel values and write each line segment
+ # as a range to the output list. If the ranges in the two input lists
+ # differ (randomly overlap) then the output list will generally be
+ # more fragmented, i.e., have more ranges of constant value. As each
+ # output range is generated compare it with the previous range to see
+ # if they can be joined, as applying a rasterop may cause two different
+ # ranges to have the same value.
+
+ x = 1
+ rn_o = RL_FIRST
+ segsize = ds - 1
+ rop_enable = false
+ plr_init (rl_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been properly performed
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, plr_nleft(d_dst))
+ if (need_src && rop_enable && plr_nleft(d_src) > 0)
+ np = min (np, plr_nleft(d_src))
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We must
+ # read the DST list whether or not we will use the data, since
+ # the list pointer must be advanced NPIX pixels so that we may
+ # copy the remainder of the list after the loop.
+
+ plr_getseg (rl_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ # Get v_src.
+ if (need_src) {
+ v_src = 0
+ if (plr_nleft (d_src) > 0)
+ plr_getseg (rl_src, d_src, np, v_src)
+
+ if (R_NOTSRC(rop)) {
+ v_src = not (v_src)
+ if (src_maxval != 0)
+ v_src = and (int(v_src), src_maxval)
+ }
+
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ }
+
+ # Get v_dst.
+ if (need_dst) {
+ if (R_NOTDST(rop)) {
+ v_dst = not (v_dst)
+ if (dst_maxval != 0)
+ v_dst = and (int(v_dst), dst_maxval)
+ }
+ }
+
+ # Apply the rasterop.
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC, PIX_NOTSRC:
+ pv = v_src
+ case PIX_DST, PIX_NOTDST:
+ pv = v_dst
+ case PIX_SRC_AND_DST, PIX_SRC_AND_NOTDST, PIX_NOTSRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST, PIX_SRC_OR_NOTDST, PIX_NOTSRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, or map int
+ # to bool for the case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (int(pv), dst_maxval)
+
+ } else
+ pv = v_dst
+
+ # Output a nonzero range.
+ if (pv > 0) {
+ p = rn_o - 1
+ if (p >= RL_FIRST &&
+ pv == rl_out[3,p] && x == rl_out[1,p] + rl_out[2,p]) {
+ # Merge new range with previous one.
+ rl_out[2,p] = rl_out[2,p] + np
+ } else {
+ rl_out[1,rn_o] = x
+ rl_out[2,rn_o] = np
+ rl_out[3,rn_o] = pv
+ rn_o = rn_o + 1
+ }
+ }
+
+ x = x + np
+ segsize = segsize - np
+ }
+
+ # Update the range list header.
+ call amovi (rl_dst, rl_out, (RL_FIRST - 1) * 3)
+ RL_LEN(rl_out) = rn_o - 1
+end
diff --git a/sys/plio/tf/plrropl.x b/sys/plio/tf/plrropl.x
new file mode 100644
index 00000000..ec936e09
--- /dev/null
+++ b/sys/plio/tf/plrropl.x
@@ -0,0 +1,195 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "../plrseg.h"
+
+# PL_RANGEROP -- Rasterop operation between source and destination range lists.
+# The indicated rasterop operation is performed upon the source and destination
+# range lists, writing the result to RL_OUT, which is a copy of RL_DST except
+# for the region affected by the rasterop operation (note that the destination
+# range list cannot be edited in place since it may change size).
+
+procedure pl_rangeropl (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+
+long rl_src[3,ARB] #I source range list
+int xs #I starting pixel index in src range list
+int src_maxval #I max pixel value in src mask
+long rl_dst[3,ARB] #I destination range list
+int ds #I starting pixel index in dst range list
+int dst_maxval #I max pixel value in dst mask
+long rl_out[3,ARB] #O output list (edited version of rl_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+bool need_src, need_dst, rop_enable
+long data, src_value, v_src, v_dst, pv
+int segsize, opcode, x, i, np, rn_o, p
+int d_src[LEN_PLRDES], d_dst[LEN_PLRDES]
+
+begin
+ need_src = R_NEED_SRC(rop)
+ need_dst = R_NEED_DST(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Advance to the desired position in the source list, discarding
+ # the unused ranges. The point XS may lie within a range or in a
+ # zero area of the input line.
+
+ if (need_src) {
+ x = 1
+ plr_init (rl_src, d_src)
+ do i = 1, ARB {
+ np = min (plr_nleft(d_src), xs - x)
+ plr_getseg (rl_src, d_src, np, v_src)
+ x = x + np
+ if (x >= xs || np == 0)
+ break
+ }
+ }
+
+ # Advance through both the source and destination lists, extracting
+ # line segments which have a constant value in each input list; the
+ # values for the two lists may differ. Apply the given rasterop to
+ # the source and destination pixel values and write each line segment
+ # as a range to the output list. If the ranges in the two input lists
+ # differ (randomly overlap) then the output list will generally be
+ # more fragmented, i.e., have more ranges of constant value. As each
+ # output range is generated compare it with the previous range to see
+ # if they can be joined, as applying a rasterop may cause two different
+ # ranges to have the same value.
+
+ x = 1
+ rn_o = RL_FIRST
+ segsize = ds - 1
+ rop_enable = false
+ plr_init (rl_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been properly performed
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, plr_nleft(d_dst))
+ if (need_src && rop_enable && plr_nleft(d_src) > 0)
+ np = min (np, plr_nleft(d_src))
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We must
+ # read the DST list whether or not we will use the data, since
+ # the list pointer must be advanced NPIX pixels so that we may
+ # copy the remainder of the list after the loop.
+
+ plr_getseg (rl_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ # Get v_src.
+ if (need_src) {
+ v_src = 0
+ if (plr_nleft (d_src) > 0)
+ plr_getseg (rl_src, d_src, np, v_src)
+
+ if (R_NOTSRC(rop)) {
+ v_src = not (v_src)
+ if (src_maxval != 0)
+ v_src = and (int(v_src), src_maxval)
+ }
+
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ }
+
+ # Get v_dst.
+ if (need_dst) {
+ if (R_NOTDST(rop)) {
+ v_dst = not (v_dst)
+ if (dst_maxval != 0)
+ v_dst = and (int(v_dst), dst_maxval)
+ }
+ }
+
+ # Apply the rasterop.
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC, PIX_NOTSRC:
+ pv = v_src
+ case PIX_DST, PIX_NOTDST:
+ pv = v_dst
+ case PIX_SRC_AND_DST, PIX_SRC_AND_NOTDST, PIX_NOTSRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST, PIX_SRC_OR_NOTDST, PIX_NOTSRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, or map int
+ # to bool for the case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (int(pv), dst_maxval)
+
+ } else
+ pv = v_dst
+
+ # Output a nonzero range.
+ if (pv > 0) {
+ p = rn_o - 1
+ if (p >= RL_FIRST &&
+ pv == rl_out[3,p] && x == rl_out[1,p] + rl_out[2,p]) {
+ # Merge new range with previous one.
+ rl_out[2,p] = rl_out[2,p] + np
+ } else {
+ rl_out[1,rn_o] = x
+ rl_out[2,rn_o] = np
+ rl_out[3,rn_o] = pv
+ rn_o = rn_o + 1
+ }
+ }
+
+ x = x + np
+ segsize = segsize - np
+ }
+
+ # Update the range list header.
+ call amovl (rl_dst, rl_out, (RL_FIRST - 1) * 3)
+ RL_LEN(rl_out) = rn_o - 1
+end
diff --git a/sys/plio/tf/plrrops.x b/sys/plio/tf/plrrops.x
new file mode 100644
index 00000000..b2d9781c
--- /dev/null
+++ b/sys/plio/tf/plrrops.x
@@ -0,0 +1,195 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plset.h>
+include <plio.h>
+include "../plrseg.h"
+
+# PL_RANGEROP -- Rasterop operation between source and destination range lists.
+# The indicated rasterop operation is performed upon the source and destination
+# range lists, writing the result to RL_OUT, which is a copy of RL_DST except
+# for the region affected by the rasterop operation (note that the destination
+# range list cannot be edited in place since it may change size).
+
+procedure pl_rangerops (rl_src, xs, src_maxval,
+ rl_dst, ds, dst_maxval, rl_out, npix, rop)
+
+short rl_src[3,ARB] #I source range list
+int xs #I starting pixel index in src range list
+int src_maxval #I max pixel value in src mask
+short rl_dst[3,ARB] #I destination range list
+int ds #I starting pixel index in dst range list
+int dst_maxval #I max pixel value in dst mask
+short rl_out[3,ARB] #O output list (edited version of rl_dst)
+int npix #I number of pixels to convert
+int rop #I rasterop
+
+bool need_src, need_dst, rop_enable
+short data, src_value, v_src, v_dst, pv
+int segsize, opcode, x, i, np, rn_o, p
+int d_src[LEN_PLRDES], d_dst[LEN_PLRDES]
+
+begin
+ need_src = R_NEED_SRC(rop)
+ need_dst = R_NEED_DST(rop)
+ opcode = R_OPCODE(rop)
+ data = R_DATA(rop)
+
+ # Pixel value to be used if input mask is boolean.
+ if (src_maxval == 1) {
+ src_value = data
+ if (src_value <= 0)
+ src_value = dst_maxval
+ }
+
+ # Advance to the desired position in the source list, discarding
+ # the unused ranges. The point XS may lie within a range or in a
+ # zero area of the input line.
+
+ if (need_src) {
+ x = 1
+ plr_init (rl_src, d_src)
+ do i = 1, ARB {
+ np = min (plr_nleft(d_src), xs - x)
+ plr_getseg (rl_src, d_src, np, v_src)
+ x = x + np
+ if (x >= xs || np == 0)
+ break
+ }
+ }
+
+ # Advance through both the source and destination lists, extracting
+ # line segments which have a constant value in each input list; the
+ # values for the two lists may differ. Apply the given rasterop to
+ # the source and destination pixel values and write each line segment
+ # as a range to the output list. If the ranges in the two input lists
+ # differ (randomly overlap) then the output list will generally be
+ # more fragmented, i.e., have more ranges of constant value. As each
+ # output range is generated compare it with the previous range to see
+ # if they can be joined, as applying a rasterop may cause two different
+ # ranges to have the same value.
+
+ x = 1
+ rn_o = RL_FIRST
+ segsize = ds - 1
+ rop_enable = false
+ plr_init (rl_dst, d_dst)
+
+ do i = 1, ARB {
+ # Set up for the next segment (before, in, and after the region to
+ # which the ROP applies), when the current segment is exhausted.
+
+ if (segsize <= 0)
+ if (!rop_enable) {
+ # Begin processing central region.
+ segsize = npix
+ rop_enable = true
+ if (segsize <= 0)
+ next
+ } else {
+ # Begin processing final region.
+ segsize = ARB
+ rop_enable = false
+ }
+
+ # Determine the length of the next output segment. This is the
+ # largest segment of constant value formed by the intersection of
+ # the two lists. If bounds checking has been properly performed
+ # then it should not be possible to see nleft=zero on either input
+ # list. Note that zeroed regions are valid data here.
+
+ np = min (segsize, plr_nleft(d_dst))
+ if (need_src && rop_enable && plr_nleft(d_src) > 0)
+ np = min (np, plr_nleft(d_src))
+ if (np <= 0)
+ break
+
+ # Get the segment value and advance the line pointers. We must
+ # read the DST list whether or not we will use the data, since
+ # the list pointer must be advanced NPIX pixels so that we may
+ # copy the remainder of the list after the loop.
+
+ plr_getseg (rl_dst, d_dst, np, v_dst)
+ if (rop_enable) {
+ # Get v_src.
+ if (need_src) {
+ v_src = 0
+ if (plr_nleft (d_src) > 0)
+ plr_getseg (rl_src, d_src, np, v_src)
+
+ if (R_NOTSRC(rop)) {
+ v_src = not (v_src)
+ if (src_maxval != 0)
+ v_src = and (int(v_src), src_maxval)
+ }
+
+ if (v_src != 0 && src_maxval == 1)
+ v_src = src_value
+ }
+
+ # Get v_dst.
+ if (need_dst) {
+ if (R_NOTDST(rop)) {
+ v_dst = not (v_dst)
+ if (dst_maxval != 0)
+ v_dst = and (int(v_dst), dst_maxval)
+ }
+ }
+
+ # Apply the rasterop.
+ switch (opcode) {
+ case PIX_CLR:
+ pv = 0
+ case PIX_SET:
+ pv = data
+ case PIX_SRC, PIX_NOTSRC:
+ pv = v_src
+ case PIX_DST, PIX_NOTDST:
+ pv = v_dst
+ case PIX_SRC_AND_DST, PIX_SRC_AND_NOTDST, PIX_NOTSRC_AND_DST:
+ pv = and (v_src, v_dst)
+ case PIX_SRC_OR_DST, PIX_SRC_OR_NOTDST, PIX_NOTSRC_OR_DST:
+ pv = or (v_src, v_dst)
+ case PIX_SRC_XOR_DST:
+ pv = xor (v_src, v_dst)
+ case PIX_NOT_SRC_AND_DST:
+ pv = not (and (v_src, v_dst))
+ case PIX_NOT_SRC_OR_DST:
+ pv = not (or (v_src, v_dst))
+ case PIX_NOT_SRC_XOR_DST:
+ pv = not (xor (v_src, v_dst))
+ }
+
+ # Mask the high bits to prevent negative values, or map int
+ # to bool for the case of a boolean output mask.
+
+ if (dst_maxval == 1 && pv != 0)
+ pv = 1
+ else if (dst_maxval > 1)
+ pv = and (int(pv), dst_maxval)
+
+ } else
+ pv = v_dst
+
+ # Output a nonzero range.
+ if (pv > 0) {
+ p = rn_o - 1
+ if (p >= RL_FIRST &&
+ pv == rl_out[3,p] && x == rl_out[1,p] + rl_out[2,p]) {
+ # Merge new range with previous one.
+ rl_out[2,p] = rl_out[2,p] + np
+ } else {
+ rl_out[1,rn_o] = x
+ rl_out[2,rn_o] = np
+ rl_out[3,rn_o] = pv
+ rn_o = rn_o + 1
+ }
+ }
+
+ x = x + np
+ segsize = segsize - np
+ }
+
+ # Update the range list header.
+ call amovs (rl_dst, rl_out, (RL_FIRST - 1) * 3)
+ RL_LEN(rl_out) = rn_o - 1
+end
diff --git a/sys/plio/zzdebug.x b/sys/plio/zzdebug.x
new file mode 100644
index 00000000..0ff3cc47
--- /dev/null
+++ b/sys/plio/zzdebug.x
@@ -0,0 +1,1442 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <xwhen.h>
+include <error.h>
+include <ctype.h>
+include <fset.h>
+include <plset.h>
+include <plio.h>
+
+task pltest = t_pltest,
+ script = t_script
+
+.help pltest
+.nf -------------------------------------------------------------------------
+PLTEST -- PL package debug and test facility.
+
+ Due to the complexity of the PL package, testing is performed using an
+interactive interpreter which reads commands from the standard input.
+All commands operate internally upon a set of four mask registers A-D,
+and a set of ten vector registers V[0-9].
+
+ a,b,c,d - mask registers
+ v0,...,v9 - vector registers
+
+The following commands are defined:
+
+ help # print command summary
+ timer # toggle timing of commands
+ run fname # read commands from a file
+ log [fname] # log commands in a file
+ clear # clear the screen
+ bye # all done (also EOF)
+
+ create [mask] naxes axlen depth # create a new mask
+ load [mask] fname # load mask from file
+ save [mask] fname [title] # save mask in file
+ loadim [mask] image # load mask from image
+ saveim [mask] image [title] # save mask in image
+ erase [mask] [vs ve] # erase a mask or region
+ draw [mask] [vs ve] [>ofile] # draw mask or region of mask
+
+ set [mask] # set reference mask
+ set [vector] i j k... # load vector register
+ show [vector] # print vector register
+ show [mask] [index] [ll] [rl] [>ofile] # print debug info for a mask
+
+ box [mask] P1 P2 rop # draw a box
+ circle [mask] P1 r rop # draw a circle
+ line [mask] P1 P2 width rop # draw a line segment
+ point [mask] P1 rop # draw a point
+ polygon [mask] P1 ... PN rop # draw a polygon
+
+ rop src [vs] dst [vs] [vn] rop # rasterop
+ stencil src [vs] dst [vs] stn [vs] [vn] rop # stencil
+ invert [mask] [vs [vn]] # invert a mask
+
+ compare mask1 mask2 # compare two masks
+ rtest mask1 mask2 # range list conversion test
+ ptest mask1 mask2 # pixel array conversion test
+ secne [mask] vs ve # test if section not empty
+ secnc [mask] vs ve # test if section not constant
+ rio [mask] vs # test mask using random i/o
+
+Rasterops may be specified either as integer constants (any radix) or via
+a simple symbolic notation, e.g.: "opcode+[value]".
+
+A mask may be examined in detail with SHOW, which calls pl_debug to decode
+the contents of a mask. A graphic image of a mask may be drawn with DRAW,
+which renders each pixel in the mask as a printable character.
+.endhelp --------------------------------------------------------------------
+
+define SZ_SBUF 512 # size limiting parameters
+define DEF_MASKSIZE_X 75
+define DEF_MASKSIZE_Y 40
+define MAXKWLEN 20
+define MAXARGS 50
+define MAXMREG 4
+define MAXVREG 10
+define MAXINCL 10
+define WIDTH 80
+
+define INT_ARG 1 # argument types
+define STRING_ARG 2
+define VECTOR_ARG 3
+define MASK_ARG 4
+
+define v_i argval[$1] # integer argument
+define v_s sbuf[argval[$1]] # string argument
+define v_si sbuf[argval[$1]+$2-1] # indexed string argument
+define v_v v_reg[1,argval[$1]+1] # vector argument
+define v_vi v_reg[$2,argval[$1]+1] # indexed vector argument
+define v_m v_mask[argval[$1]] # mask argument
+
+define KW_BOX 1 # interpreter commands
+define KW_BYE 2
+define KW_CIRCLE 3
+define KW_CLEAR 4
+define KW_COMPARE 5
+define KW_CREATE 6
+define KW_DRAW 7
+define KW_ERASE 8
+define KW_HELP 9
+# eol 10
+define KW_LINE 11
+define KW_POINT 12
+define KW_POLYGON 13
+define KW_LOAD 14
+define KW_LOADIM 15
+define KW_PTEST 16
+define KW_ROP 17
+define KW_RTEST 18
+define KW_RUN 19
+define KW_SAVE 20
+define KW_SAVEIM 21
+# eol 22
+define KW_SET 23
+define KW_SHOW 24
+define KW_SECNE 25
+define KW_SECNC 26
+define KW_RIO 27
+define KW_STENCIL 28
+define KW_INVERT 29
+define KW_TIMER 30
+define KW_LOG 31
+
+
+# PLTEST -- Test the PL package. Read and execute commands from the standard
+# input until EOF or BYE is seen.
+
+procedure t_pltest()
+
+long time[2]
+int x, y, r
+bool timer, bval
+int px[MAXARGS], py[MAXARGS], old_onint, width, mval
+pointer pl, pl_1, pl_2, def_pl, pl_src, pl_dst, pl_stn, tty, plr
+char cmd[SZ_LINE], kwname[SZ_FNAME], title[SZ_LINE], fname[SZ_FNAME]
+int what, rop, v_arg, x1, x2, y1, y2, ip, op, ch, i, j, cmdlog, status
+int opcode, save_fd[MAXINCL], in, fd, o_fd, maskno, depth, naxes, npts
+int v1[PL_MAXDIM], v2[PL_MAXDIM], v3[PL_MAXDIM], v4[PL_MAXDIM], v[PL_MAXDIM]
+
+int plr_getpix(), pl_compare()
+int fstati(), strdic(), open(), getline(), strncmp(), locpr()
+pointer pl_create(), ttyodes(), plr_open()
+bool pl_sectnotempty(), pl_sectnotconst()
+extern onint()
+
+char sbuf[SZ_SBUF]
+int nargs, argno, argtype[MAXARGS], argval[MAXARGS], s_op
+int v_mask[MAXMREG], v_reg[PL_MAXDIM,MAXVREG], jmpbuf[LEN_JUMPBUF]
+common /plzcom/ v_mask, v_reg, nargs, argno, argtype, argval, s_op,
+ jmpbuf, sbuf
+define argerr_ 91
+define eof_ 92
+
+string keywords "|box|bye|circle|clear|compare|create|draw|erase|help|\
+ |line|point|polygon|load|loadim|ptest|rop|rtest|run|save|saveim|\
+ |set|show|secne|secnc|rio|stencil|invert|timer|log|"
+
+begin
+ in = 0
+ ip = 1
+ fd = STDIN
+ cmdlog = 0
+ cmd[ip] = EOS
+ timer = false
+
+ # Initialize the mask registers.
+ v[1] = DEF_MASKSIZE_X
+ v[2] = DEF_MASKSIZE_Y
+ do i = 1, MAXMREG
+ v_mask[i] = pl_create (2, v, 7)
+ def_pl = v_mask[1]
+
+ # Initialize the vector registers.
+ do i = 1, MAXVREG
+ call amovki (1, v_reg[1,i], PL_MAXDIM)
+
+ # Main interpreter loop.
+ # ---------------------------
+
+ call xwhen (X_INT, locpr(onint), old_onint)
+ call zsvjmp (jmpbuf, status)
+
+ if (status != OK) {
+ # Clean up after an interrupt.
+ call xer_reset()
+ ip = 1; cmd[ip] = EOS
+ while (in > 0) {
+ call close (fd)
+ fd = save_fd[in]
+ in = in - 1
+ }
+ }
+
+ repeat {
+ # Get next command.
+ if (cmd[ip] == '\n' || cmd[ip] == '#' || cmd[ip] == EOS) {
+ # Prompt if reading from the standard input.
+ if (in == 0 && fstati (STDIN, F_REDIR) == NO) {
+ call putline (STDOUT, "* ")
+ call flush (STDOUT)
+ }
+
+ # Handle EOF on the current command stream.
+ if (getline (fd, cmd) == EOF) {
+eof_ if (in > 0) {
+ call close (fd)
+ fd = save_fd[in]
+ in = in - 1
+ } else
+ break
+ }
+
+ ip = 1
+ }
+
+ # Skip blank lines and comment lines.
+ for (ip=1; IS_WHITE(cmd[ip]) || cmd[ip] == ';'; ip=ip+1)
+ ;
+ if (cmd[ip] == '\n' || cmd[ip] == '#' || cmd[ip] == EOS)
+ next
+
+ if (cmdlog != 0)
+ call putline (cmdlog, cmd)
+
+ # Extract the keyword into the KWNAME buffer. Leave the input
+ # pointer positioned to the first char following the keyword.
+
+ for (op=1; cmd[ip] != EOS && cmd[ip] != '\n'; ip=ip+1) {
+ ch = cmd[ip]
+ if (IS_ALNUM(ch) || ch == '?' || ch == '_') {
+ kwname[op] = ch
+ op = op + 1
+ } else
+ break
+ }
+ kwname[op] = EOS
+
+ # Look up the keyword in the dictionary. If not found ring the
+ # bell, but do not quit.
+
+ if (kwname[1] == '?')
+ opcode = KW_HELP
+ else
+ opcode = strdic (kwname, kwname, MAXKWLEN, keywords)
+ if (opcode <= 0) {
+ call eprintf ("unknown command\007\n")
+ call flush (STDERR)
+ ip=1; cmd[ip] = EOS
+ next
+ }
+
+ # Parse the argument list.
+ call parse_args (cmd, ip)
+
+ # Process the command.
+ # -------------------------
+
+ switch (opcode) {
+ case KW_BYE:
+ goto eof_
+
+ case KW_LOG:
+ # Enable/Disable command logging.
+
+ if (cmdlog == 0) {
+ if (nargs >= 1 && argtype[argno] == STRING_ARG)
+ cmdlog = open (v_s(argno), APPEND, TEXT_FILE)
+ else
+ cmdlog = open ("zzdebug.log", APPEND, TEXT_FILE)
+ call eprintf ("command spooling enabled\n")
+ call fprintf (cmdlog, "# --- begin ---\n")
+ } else {
+ call close (cmdlog)
+ cmdlog = 0
+ call eprintf ("command spooling disabled\n")
+ }
+
+ case KW_POINT:
+ # Draw a point.
+
+ # Get mask.
+ pl = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get coords of point.
+ if (argtype[argno] == VECTOR_ARG) {
+ # Coords given as vectors.
+ x1 = v_vi(argno,1)
+ y1 = v_vi(argno,2)
+ argno = argno + 1
+ } else {
+ # Coords given explicitly.
+ do i = 1, 2
+ if (argtype[argno+i-1] != INT_ARG)
+ goto argerr_
+
+ x1 = v_i(argno); argno = argno + 1
+ y1 = v_i(argno); argno = argno + 1
+ }
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = or (PIX_SRC, PIX_DST) + PIX_VALUE('1')
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_point (pl, x1, y1, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_BOX:
+ # Draw a box.
+
+ # Get mask.
+ pl = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get corner coords of box.
+ if (argtype[argno] == VECTOR_ARG) {
+ # Coords given as vectors.
+ x1 = v_vi(argno,1)
+ y1 = v_vi(argno,2)
+ argno = argno + 1
+ if (argtype[argno] != VECTOR_ARG)
+ goto argerr_
+ x2 = v_vi(argno,1)
+ y2 = v_vi(argno,2)
+ argno = argno + 1
+
+ } else {
+ # Coords given explicitly.
+ do i = 1, 4
+ if (argtype[argno+i-1] != INT_ARG)
+ goto argerr_
+
+ x1 = v_i(argno); argno = argno + 1
+ y1 = v_i(argno); argno = argno + 1
+ x2 = v_i(argno); argno = argno + 1
+ y2 = v_i(argno); argno = argno + 1
+ }
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = or (PIX_SRC, PIX_DST) + PIX_VALUE('2')
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_box (pl, x1, y1, x2, y2, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_LINE:
+ # Draw a line of arbitrary orientation and width.
+
+ # Get mask.
+ pl = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get endpoints of line.
+ if (argtype[argno] == VECTOR_ARG) {
+ # Coords given as vectors.
+ x1 = v_vi(argno,1)
+ y1 = v_vi(argno,2)
+ argno = argno + 1
+ if (argtype[argno] != VECTOR_ARG)
+ goto argerr_
+ x2 = v_vi(argno,1)
+ y2 = v_vi(argno,2)
+ argno = argno + 1
+
+ } else {
+ # Coords given explicitly.
+ do i = 1, 4
+ if (argtype[argno+i-1] != INT_ARG)
+ goto argerr_
+
+ x1 = v_i(argno); argno = argno + 1
+ y1 = v_i(argno); argno = argno + 1
+ x2 = v_i(argno); argno = argno + 1
+ y2 = v_i(argno); argno = argno + 1
+ }
+
+ # Get line width.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ width = v_i(argno); argno = argno + 1
+ } else
+ width = 1
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = or (PIX_SRC, PIX_DST) + PIX_VALUE('4')
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_line (pl, x1, y1, x2, y2, width, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_CIRCLE:
+ # Draw a circle.
+
+ # Get mask.
+ pl = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get center coords and radius of circle.
+ if (argtype[argno] == VECTOR_ARG) {
+ # Center coords given as a vector.
+ x = v_vi(argno,1)
+ y = v_vi(argno,2)
+ argno = argno + 1
+
+ } else {
+ # Center coords given explicitly.
+ do i = 1, 2
+ if (argtype[argno+i-1] != INT_ARG)
+ goto argerr_
+
+ x = v_i(argno); argno = argno + 1
+ y = v_i(argno); argno = argno + 1
+ }
+
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ r = v_i(argno); argno = argno + 1
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = or (PIX_SRC, PIX_DST) + PIX_VALUE('Q')
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_circle (pl, x, y, r, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_POLYGON:
+ # Draw a polygon.
+
+ # Get mask.
+ pl = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get the coordinates of the polygon.
+ for (npts=0; argno <= nargs; ) {
+ npts = npts + 1
+
+ if (argtype[argno] == VECTOR_ARG) {
+ # Coords of point given as a vector.
+ px[npts] = v_vi(argno,1)
+ py[npts] = v_vi(argno,2)
+ argno = argno + 1
+
+ } else if (argtype[argno] == INT_ARG &&
+ argtype[argno+1] == INT_ARG) {
+
+ # Center coords given explicitly.
+ px[npts] = v_i(argno); argno = argno + 1
+ py[npts] = v_i(argno); argno = argno + 1
+ }
+ }
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = or (PIX_SRC, PIX_DST) + PIX_VALUE('R')
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_polygon (pl, px, py, npts, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_CLEAR:
+ # Clear the screen.
+ tty = ttyodes ("terminal")
+ call ttyclear (STDOUT, tty)
+ call ttycdes (tty)
+
+ case KW_COMPARE:
+ # Compare two masks.
+ if (nargs < 2)
+ goto argerr_
+
+ # Get mask 1.
+ if (argtype[argno] == MASK_ARG) {
+ pl_1 = v_m(argno)
+ argno = argno + 1
+ } else
+ goto argerr_
+
+ # Get mask 2.
+ if (argtype[argno] == MASK_ARG) {
+ pl_2 = v_m(argno)
+ argno = argno + 1
+ } else
+ goto argerr_
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ status = pl_compare (pl_1, pl_2, STDOUT)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_CREATE:
+ # Create a new, emtpy mask of the given size and depth.
+
+ # Get mask.
+ maskno = 1
+ if (argtype[argno] == MASK_ARG) {
+ maskno = v_i(argno)
+ argno = argno + 1
+ }
+
+ # Get naxes.
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ naxes = v_i(argno); argno = argno + 1
+
+ # Get mask size.
+ if (argtype[argno] == VECTOR_ARG) {
+ # Mask size given as vector.
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else {
+ # Mask size given explicitly.
+ do i = 1, naxes {
+ if (argtype[argno+i-1] != INT_ARG)
+ goto argerr_
+ v1[i] = v_i(argno)
+ argno = argno + 1
+ }
+ }
+
+ # Get mask depth.
+ if (argtype[argno] != INT_ARG)
+ depth = 1
+ else {
+ depth = v_i(argno)
+ argno = argno + 1
+ }
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_close (v_mask[maskno])
+ v_mask[maskno] = pl_create (naxes, v1, depth)
+ def_pl = v_mask[maskno]
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_DRAW:
+ # Draw a mask or region of a mask on the screen.
+
+ # Get mask.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get vector coords of region to be drawn.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl,1), v2, PL_MAXDIM)
+
+ # Get output stream.
+ o_fd = STDOUT
+ if (argtype[argno] == STRING_ARG) {
+ if (v_si(argno,1) == '>') {
+ # Write output to a file.
+ if (v_si(argno,2) == '>') {
+ iferr (o_fd = open (v_si(argno,3),
+ APPEND, TEXT_FILE)) {
+ call erract (EA_WARN)
+ o_fd = STDOUT
+ }
+ } else {
+ iferr (o_fd = open (v_si(argno,2),
+ NEW_FILE, TEXT_FILE)) {
+ call erract (EA_WARN)
+ o_fd = STDOUT
+ }
+ }
+ } else {
+ call eprintf ("unknown option `%s'\n")
+ call pargstr (v_s(argno))
+ }
+ argno = argno + 1
+ }
+
+ # Perform the operation.
+ call pl_asciidump (pl, v1, v2, o_fd)
+
+ if (o_fd != STDOUT)
+ call close (o_fd)
+
+ case KW_ERASE:
+ # Erase a mask, or a region of a mask.
+
+ # Get mask.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get vector coords of region to be erased.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl,1), v2, PL_MAXDIM)
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ if (nargs <= 1)
+ call pl_clear (pl)
+ else
+ call pl_rop (NULL, 0, pl, v1, v2, PIX_CLR)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_HELP:
+ # Print a command summary.
+ call print_help (STDOUT)
+
+ case KW_RUN:
+ # Read commands from a file.
+ if (nargs < 1 || argtype[argno] != STRING_ARG)
+ goto argerr_
+
+ in = in + 1
+ if (in > MAXINCL)
+ call error (1, "too many nested run files\n")
+ save_fd[in] = fd
+ iferr (fd = open (v_s(argno), READ_ONLY, TEXT_FILE)) {
+ call erract (EA_WARN)
+ fd = save_fd[in]
+ in = in - 1
+ }
+
+ case KW_SET:
+ # Set the value of a mask or vector register.
+ if (nargs < 1) {
+ goto argerr_
+
+ } else if (argtype[argno] == MASK_ARG) {
+ # Set the default mask.
+ def_pl = v_m(argno)
+ maskno = v_i(argno)
+
+ } else if (argtype[argno] == VECTOR_ARG) {
+ # Set a vector register.
+ v_arg = argno
+ argno = argno + 1
+
+ do i = 1, PL_MAXDIM
+ if (argno <= nargs && argtype[argno] == INT_ARG) {
+ v[i] = v_i(argno)
+ argno = argno + 1
+ } else
+ v[i] = 1
+
+ call amovi (v, v_v(v_arg), PL_MAXDIM)
+ }
+
+ case KW_SHOW:
+ # Print information about a mask or vector register.
+
+ if (nargs < 1 || argtype[argno] == MASK_ARG) {
+ # Print information about a mask.
+
+ if (nargs < 1)
+ pl = def_pl
+ else {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ o_fd = STDOUT
+
+ # Process option selects.
+ what = PD_SUMMARY
+ while (argno <= nargs && argtype[argno] == STRING_ARG) {
+ if (strncmp (v_s(argno), "i", 1) == 0) {
+ what = or (what, PD_INDEX)
+ } else if (strncmp (v_s(argno), "ll", 2) == 0) {
+ what = or (what, PD_LLOUT)
+ } else if (strncmp (v_s(argno), "rl", 2) == 0) {
+ what = or (what, PD_RLOUT)
+ } else if (strncmp (v_s(argno), "lh", 2) == 0) {
+ what = or (what, PD_LHDR)
+
+ } else if (v_si(argno,1) == '>') {
+ # Write output to a file.
+ if (v_si(argno,2) == '>') {
+ iferr (o_fd = open (v_si(argno,3),
+ APPEND, TEXT_FILE)) {
+ call erract (EA_WARN)
+ o_fd = STDOUT
+ }
+ } else {
+ iferr (o_fd = open (v_si(argno,2),
+ NEW_FILE, TEXT_FILE)) {
+ call erract (EA_WARN)
+ o_fd = STDOUT
+ }
+ }
+ } else {
+ call eprintf ("unknown option `%s'\n")
+ call pargstr (v_s(argno))
+ }
+ argno = argno + 1
+ }
+
+ # Perform the operation.
+ call pl_debug (pl, o_fd, WIDTH, what)
+ if (o_fd != STDOUT)
+ call close (o_fd)
+
+ } else if (argtype[argno] == VECTOR_ARG) {
+ # Print the value of a vector register.
+ call printf ("v%d: ")
+ call pargi (v_i(argno))
+ do i = 1, PL_MAXDIM {
+ call printf (" %d")
+ call pargi (v_vi(argno,i))
+ }
+ call printf ("\n")
+
+ } else {
+ # Print the value of all vector registers.
+ do j = 1, MAXVREG {
+ call printf ("v%d: ")
+ call pargi (j-1)
+ do i = 1, PL_MAXDIM {
+ call printf (" %d")
+ call pargi (v_reg(i,j))
+ }
+ call printf ("\n")
+ }
+ }
+
+ case KW_LOAD:
+ # Load a mask from a file.
+
+ # Get mask to be loaded.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get mask filename.
+ if (argno > nargs || argtype[argno] != STRING_ARG)
+ goto argerr_
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ iferr (call pl_loadf (pl, v_s(argno), title, SZ_LINE))
+ call erract (EA_WARN)
+ else if (title[1] != EOS) {
+ call printf ("mask: %s\n")
+ call pargstr (title)
+ call flush (STDOUT)
+ }
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_SAVE:
+ # Save a mask in a file.
+
+ # Get mask to be saved.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get mask filename.
+ if (argno > nargs || argtype[argno] != STRING_ARG)
+ goto argerr_
+ else {
+ call strcpy (v_s(argno), fname, SZ_FNAME)
+ argno = argno + 1
+ }
+
+ # Get title string.
+ if (argno <= nargs && argtype[argno] == STRING_ARG) {
+ call strcpy (v_s(argno), title, SZ_LINE)
+ argno = argno + 1
+ }
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ iferr (call pl_savef (pl, fname, title, 0))
+ call erract (EA_WARN)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_LOADIM:
+ # Load a mask from an image.
+
+ # Get mask to be loaded.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get image section.
+ if (argno > nargs || argtype[argno] != STRING_ARG)
+ goto argerr_
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ iferr (call pl_loadim (pl, v_s(argno), title, SZ_LINE))
+ call erract (EA_WARN)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_SAVEIM:
+ # Save a mask in an image.
+
+ # Get mask to be saved.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get output image name.
+ if (argno > nargs || argtype[argno] != STRING_ARG)
+ goto argerr_
+ else {
+ call strcpy (v_s(argno), fname, SZ_FNAME)
+ argno = argno + 1
+ }
+
+ # Get title string.
+ if (argno <= nargs && argtype[argno] == STRING_ARG) {
+ call strcpy (v_s(argno), title, SZ_LINE)
+ argno = argno + 1
+ }
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ iferr (call pl_saveim (pl, fname, title, 0))
+ call erract (EA_WARN)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_ROP:
+ # General rasterop operation.
+
+ # Get source mask.
+ pl_src = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl_src = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get start vector in source mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ # Get destination mask.
+ pl_dst = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl_dst = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get start vector in destination mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v2, PL_MAXDIM)
+
+ # Get vector defining size of region to be modified.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v3, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl_dst,1), v3, PL_MAXDIM)
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else
+ rop = PIX_SRC
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_rop (pl_src, v1, pl_dst, v2, v3, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_STENCIL:
+ # Rasterop operation though a stencil mask.
+
+ # Get source mask.
+ pl_src = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl_src = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get start vector in source mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ # Get destination mask.
+ pl_dst = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl_dst = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get start vector in destination mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v2, PL_MAXDIM)
+
+ # Get stencil mask.
+ pl_stn = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl_stn = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get start vector in stencil mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v3, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v3, PL_MAXDIM)
+
+ # Get vector defining size of region to be modified.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v4, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl_dst,1), v4, PL_MAXDIM)
+
+ # Get rop.
+ if (argno <= nargs) {
+ if (argtype[argno] != INT_ARG)
+ goto argerr_
+ rop = v_i(argno); argno = argno + 1
+ } else {
+ call eprintf ("no rop specified - copying src to dst\n")
+ rop = PIX_SRC
+ }
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call pl_stencil (pl_src, v1, pl_dst, v2, pl_stn, v3, v4, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_INVERT:
+ # Invert a mask.
+
+ # Get mask.
+ pl_src = def_pl
+ if (argtype[argno] == MASK_ARG) {
+ pl_src = v_m(argno)
+ argno = argno + 1
+ }
+ pl_dst = pl_src
+
+ # Get start vector in mask.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ # Get vector defining size of region to be modified.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl_dst,1), v2, PL_MAXDIM)
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ rop = PIX_NOT(PIX_SRC)
+ call pl_rop (pl_src, v1, pl_dst, v1, v2, rop)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_PTEST, KW_RTEST:
+ # Line list to pixel array or range list conversion test.
+ if (nargs < 2)
+ goto argerr_
+
+ # Get mask 1.
+ if (argtype[argno] == MASK_ARG) {
+ pl_1 = v_m(argno)
+ argno = argno + 1
+ } else
+ goto argerr_
+
+ # Get mask 2.
+ if (argtype[argno] == MASK_ARG) {
+ pl_2 = v_m(argno)
+ argno = argno + 1
+ } else
+ goto argerr_
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+ call conv_test (pl_1, pl_2, STDOUT, opcode)
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_SECNE:
+ # Test if a section of a mask is not empty.
+
+ # Get mask.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get vector coords of region to be erased.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl,1), v2, PL_MAXDIM)
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+
+ bval = pl_sectnotempty (pl, v1, v2, 2)
+ call printf ("pl_sectnotempty -> %b (mask is %s)\n")
+ call pargb (bval)
+ if (bval)
+ call pargstr ("not empty")
+ else
+ call pargstr ("empty")
+
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_SECNC:
+ # Test if a section of a mask is not constant.
+
+ # Get mask.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get vector coords of region to be erased.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v2, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovi (PL_AXLEN(pl,1), v2, PL_MAXDIM)
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+
+ call printf ("pl_sectnotconst -> ")
+ if (pl_sectnotconst (pl, v1, v2, 2, mval))
+ call printf ("true (mask is not constant)\n")
+ else {
+ call printf ("false (mask is constant, value=%d)\n")
+ call pargi (mval)
+ }
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_RIO:
+ # Test access to a mask using random io (plr_getpix).
+
+ # Get mask.
+ pl = def_pl
+ if (nargs >= 1 && argtype[argno] == MASK_ARG) {
+ pl = v_m(argno)
+ argno = argno + 1
+ }
+
+ # Get vector coords of region to be tested.
+ if (argtype[argno] == VECTOR_ARG) {
+ call amovi (v_v(argno), v1, PL_MAXDIM)
+ argno = argno + 1
+ } else
+ call amovki (1, v1, PL_MAXDIM)
+
+ # Perform the operation.
+ if (timer)
+ call sys_mtime (time)
+
+ call amovki (1, v, PL_MAXDIM)
+ plr = plr_open (pl, v, 0)
+ call printf ("mask pixel [%d,%d] has value %d\n")
+ call pargi (v1[1])
+ call pargi (v1[2])
+ call pargi (plr_getpix (plr, v1[1], v1[2]))
+ call plr_close (plr)
+
+ if (timer)
+ call sys_ptime (STDOUT, "", time)
+
+ case KW_TIMER:
+ if (timer) {
+ call printf ("timer off\n")
+ timer = false
+ } else {
+ call printf ("timer on\n")
+ timer = true
+ }
+
+ default:
+ # Unrecognized command.
+ call eprintf ("unknown switch\007\n")
+ call flush (STDERR)
+ }
+
+ call flush (STDOUT)
+ if (cmdlog != 0)
+ call flush (cmdlog)
+ next
+argerr_
+ call eprintf ("invalid argument list\n")
+ }
+
+ call zxwhen (X_INT, old_onint, status)
+ if (cmdlog != 0)
+ call close (cmdlog)
+
+ do i = 1, MAXMREG
+ call pl_close (v_mask[i])
+end
+
+
+# ONINT -- Interrupt handler.
+
+procedure onint (signal, next_handler)
+
+int signal #I signal code
+int next_handler #O epa of next handler
+
+char sbuf[SZ_SBUF]
+int nargs, argno, argtype[MAXARGS], argval[MAXARGS], s_op
+int v_mask[MAXMREG], v_reg[PL_MAXDIM,MAXVREG], jmpbuf[LEN_JUMPBUF]
+common /plzcom/ v_mask, v_reg, nargs, argno, argtype, argval, s_op,
+ jmpbuf, sbuf
+
+begin
+ call fseti (STDOUT, F_CANCEL, OK)
+ call eprintf ("interrupt!\007\n")
+ call zdojmp (jmpbuf, signal)
+end
+
+
+# PARSE_ARGS -- Parse the argument list to an interpreter command, leaving
+# the decoded arguments in the interpreter common, and returning the number
+# of arguments as the function value.
+
+procedure parse_args (args, ip)
+
+char args[ARB] # argument list
+int ip # pointer into argument list
+
+double dval
+int nchars, junk, i
+int ctowrd(), stridx(), gctod(), strlen()
+
+char sbuf[SZ_SBUF]
+int nargs, argno, argtype[MAXARGS], argval[MAXARGS], s_op
+int v_mask[MAXMREG], v_reg[PL_MAXDIM,MAXVREG], jmpbuf[LEN_JUMPBUF]
+common /plzcom/ v_mask, v_reg, nargs, argno, argtype, argval, s_op,
+ jmpbuf, sbuf
+
+begin
+ s_op = 1
+ argno = 1
+ nargs = 0
+
+ do i = 1, MAXARGS
+ argtype[i] = 0
+
+ # Get next token.
+ junk = ctowrd (args, ip, sbuf[s_op], SZ_SBUF-s_op)
+ nchars = strlen (sbuf[s_op])
+
+ while (nchars > 0) {
+ nargs = nargs + 1
+ if (nargs > MAXARGS)
+ call error (1, "too many arguments")
+
+ if (nchars == 1 && sbuf[s_op] == '=') {
+ # Discard assignment operator.
+ nargs = nargs - 1
+
+ } else if (nchars == 1 && stridx (sbuf[s_op], "abcd") > 0) {
+ # Mask register.
+ argval[nargs] = stridx (sbuf[s_op], "abcd")
+ argtype[nargs] = MASK_ARG
+
+ } else if (nchars == 2 && sbuf[s_op] == 'v' &&
+ # Vector register.
+ IS_DIGIT(sbuf[s_op+1])) {
+ argval[nargs] = TO_INTEG(sbuf[s_op+1])
+ argtype[nargs] = VECTOR_ARG
+
+ } else if (IS_DIGIT (sbuf[s_op])) {
+ # Get an integer constant.
+ i=1; nchars = gctod (sbuf[s_op], i, dval)
+ argval[nargs] = nint(dval)
+ argtype[nargs] = INT_ARG
+
+ # Handle the notation "opcode+value", for rasterops.
+ if (sbuf[s_op+i-1] == '+') {
+ i=i+1; nchars = gctod (sbuf[s_op], i, dval)
+ argval[nargs] = argval[nargs] + PIX_VALUE(nint(dval))
+ }
+
+ } else {
+ # String constant.
+ argval[nargs] = s_op
+ argtype[nargs] = STRING_ARG
+ s_op = s_op + nchars + 1
+ }
+
+ while (IS_WHITE(args[ip]))
+ ip = ip + 1
+ if (args[ip] == ';' || args[ip] == '\n') {
+ ip = ip + 1
+ break
+ }
+
+ # Get next token.
+ junk = ctowrd (args, ip, sbuf[s_op], SZ_SBUF-s_op)
+ nchars = strlen (sbuf[s_op])
+ }
+end
+
+
+# CONV_TEST -- Test the line list to pixel array or range list conversion
+# routines.
+
+procedure conv_test (pl_1, pl_2, fd, opcode)
+
+pointer pl_1 #I input mask
+pointer pl_2 #I output mask
+int fd #I output file, for reporting errors
+int opcode #I KW_[PR]TEST
+
+begin
+ call fprintf (fd, "conv_test called\n")
+end
+
+
+# PRINT_HELP -- Print the PL test interpreter commands help summary.
+
+procedure print_help (fd)
+
+int fd #I output file
+
+begin
+ call fprintf (fd, "help%48t# print command summary\n")
+ call fprintf (fd, "timer%48t# toggle timing of commands\n")
+ call fprintf (fd, "run fname%48t# read commands from a file\n")
+ call fprintf (fd, "log [fname]%48t# log commands in a file\n")
+ call fprintf (fd, "clear%48t# clear the screen\n")
+ call fprintf (fd, "bye%48t# all done (also EOF)\n\n")
+ call fprintf (fd,
+ "create [mask] naxes axlen [depth]%48t# create a new mask\n")
+ call fprintf (fd, "load [mask] fname%48t# load mask from file\n")
+ call fprintf (fd, "save [mask] fname%48t# save mask in file\n")
+ call fprintf (fd, "loadim [mask] image%48t# load mask from image\n")
+ call fprintf (fd, "saveim [mask] image%48t# save mask in image\n")
+ call fprintf (fd, "erase [mask] [vs ve]%48t# erase a mask or region\n")
+ call fprintf (fd,
+ "draw [mask] [vs ve] [>ofile]%48t# draw mask or region of mask\n\n")
+ call fprintf (fd, "set [mask]%48t# set reference mask\n")
+ call fprintf (fd, "set [vector] i j k...%48t# load vector register\n")
+ call fprintf (fd, "show [vector]%48t# print vector register\n")
+ call fprintf (fd,
+"show [mask] [index] [ll] [rl] [>ofile]%48t# print debug info for a mask\n\n")
+ call fprintf (fd, "box P1 P2 rop%48t# draw a box\n")
+ call fprintf (fd, "circle P1 r rop%48t# draw a circle\n \n")
+ call fprintf (fd,
+ "line [mask] P1 P2 width rop%48t# draw a line segment\n")
+ call fprintf (fd, "point [mask] P1 rop%48t# draw a point\n")
+ call fprintf (fd, "polygon [mask] P1 ... PN rop%48t# draw a polygon\n")
+ call fprintf (fd, "rop src [vs] dst [vs] [vn] rop%48t# rasterop\n")
+ call fprintf (fd,
+ "stencil src [vs] dst [vs] stn [vs] [vn] rop%48t# stencil\n \n")
+ call fprintf (fd, "compare mask1 mask2%48t# compare two masks\n")
+ call fprintf (fd, "rtest mask1 mask2%48t# range list conversion test\n")
+ call fprintf (fd,
+ "ptest mask1 mask2%48t# pixel array conversion test\n")
+ call fprintf (fd, "secne [mask] [vs ve]%48t# test section not empty\n")
+ call fprintf (fd, "rio [mask] [vs]%48t# test mask using random i/o\n")
+end
+
+
+# SCRIPT -- Make a PLIO drawing script suitable to input to PLTEST above.
+
+procedure t_script()
+
+int ncmds, seed, i
+int xo, yo, xs, ys, x, y, r
+int clgeti()
+real urand()
+
+begin
+ ncmds = clgeti ("ncmds")
+
+ xo = 50; xs = 1024 - 100
+ yo = 50; ys = 1024 - 100
+
+ seed = 5
+
+ do i = 1, ncmds {
+ x = urand(seed) * xs + xo
+ y = urand(seed) * ys + yo
+ r = urand(seed) * 40
+
+ call printf ("circle %d %d %d %d\n")
+ call pargi (x)
+ call pargi (y)
+ call pargi (r)
+ call pargi (PIX_SET + PIX_VALUE(mod(i,256)))
+
+ call printf ("box %d %d %d %d %d\n")
+ call pargi (x - r)
+ call pargi (y - r)
+ call pargi (x + r)
+ call pargi (y + r)
+ call pargi (or(PIX_SRC,PIX_DST) + PIX_VALUE(mod(i*2,256)))
+
+ call printf ("point %d %d %d\n")
+ call pargi (x)
+ call pargi (y)
+ call pargi (PIX_CLR + PIX_VALUE(mod(i*4,256)))
+ }
+end
diff --git a/sys/plio/zzlib.x b/sys/plio/zzlib.x
new file mode 100644
index 00000000..fe21d849
--- /dev/null
+++ b/sys/plio/zzlib.x
@@ -0,0 +1,64 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <plio.h>
+
+# PL_CHKFREE -- Verify that the count of free space in the linelist is
+# correct. (DEBUG TOOL).
+
+procedure pl_chkfree (pl, msg)
+
+pointer pl #I mask descriptor
+char msg[ARB] #I message to be printed if error occurs
+
+pointer o_lp
+short nref[8192], nl
+int used, free, nused, nfree, b_len, i
+
+begin
+ used = 0; nused = 0
+ free = 0; nfree = 0
+
+ # Count the space in the active line lists.
+ nl = 0
+ for (i=0; i < PL_LLOP(pl); i=i+b_len) {
+ o_lp = Ref (pl, i)
+ b_len = LP_BLEN(o_lp)
+
+ nl = nl + 1
+ nref[nl] = LP_NREF(o_lp)
+
+ if (LP_NREF(o_lp) < 0) {
+ call eprintf ("lineoff %d, nref = %d\n")
+ call pargi (i)
+ call pargi (LP_NREF(o_lp))
+ free = free + b_len
+ nfree = nfree + 1
+ } else if (i == PL_EMPTYLINE || LP_NREF(o_lp) > 0) {
+ used = used + b_len
+ nused = nused + 1
+ } else {
+ free = free + b_len
+ nfree = nfree + 1
+ }
+ }
+
+ if (free != PL_LLFREE(pl)) {
+ call eprintf ("CHKFREE (%s): used=%d,%d, free=%d,%d, ")
+ call pargstr (msg)
+ call pargi (used)
+ call pargi (nused)
+ call pargi (free)
+ call pargi (nfree)
+ call eprintf ("PL_LLFREE=%d, OP-FREE=%d\n")
+ call pargi (PL_LLFREE(pl))
+ call pargi (PL_LLOP(pl) - PL_LLFREE(pl))
+
+ do i = 1, nl {
+ call eprintf ("%4d")
+ call pargs (nref[i])
+ if (mod (i,19) == 0)
+ call eprintf ("\n")
+ }
+ call eprintf ("\n")
+ }
+end
diff --git a/sys/plio/zzsum.x b/sys/plio/zzsum.x
new file mode 100644
index 00000000..2e6097f9
--- /dev/null
+++ b/sys/plio/zzsum.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <pmset.h>
+
+task sum = t_sum
+
+# SUM -- Sum the image pixels lying within the given mask.
+
+procedure t_sum()
+
+char image[SZ_FNAME] # input data image
+char mask[SZ_FNAME] # image mask
+
+int npix, mval, totpix, m_flags
+long v[PM_MAXDIM]
+pointer im, mp, pp
+real sum
+
+bool clgetb()
+real asumr()
+int mio_glsegr()
+pointer immap(), mio_open()
+
+begin
+ call clgstr ("image", image, SZ_FNAME)
+ call clgstr ("mask", mask, SZ_FNAME)
+ m_flags = 0
+ if (clgetb ("invert"))
+ m_flags = INVERT_MASK
+
+ im = immap (image, READ_ONLY, 0)
+ mp = mio_open (mask, m_flags, im)
+
+ sum = 0; totpix = 0
+ while (mio_glsegr (mp, pp, mval, v, npix) != EOF) {
+ sum = sum + asumr (Memr[pp], npix)
+ totpix = totpix + npix
+ }
+
+ call mio_close (mp)
+ call imunmap (im)
+
+ call printf ("%d pixels, sum=%g, mean=%g\n")
+ call pargi (totpix)
+ call pargr (sum)
+ if (totpix > 0)
+ call pargr (sum / totpix)
+ else
+ call pargr (INDEF)
+end