[alt.sources] Montage

cristy@eplrx7.uucp (John Cristy) (05/25/91)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README montage.man Imakefile Makefile montage.c
# Wrapped by cristy@dupont.com on Fri May 17 13:39:55 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(342 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
Montage creates a composite image by combining several separate
images.  The images are tiled on the composite image with the name of
the image appearing just above the individual tile.  Montage requires
ImageMagick.  It was just recently posted to comp.sources.x.  It can
also be obtained as contrib/ImageMagick.tar.Z on export.lcs.mit.edu.
END_OF_FILE
if test 342 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'montage.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'montage.man'\"
else
echo shar: Extracting \"'montage.man'\" \(26687 characters\)
sed "s/^X//" >'montage.man' <<'END_OF_FILE'
X.ad l
X.nh
X.TH ANIMATE 1 "1 January 1991" "X Version 11"
X.SH NAME
montage - creates a composite image by combining several separate images
X.SH SYNOPSIS
X.B "montage" [ \fIoptions\fP ...] \fIfile\fP
X[ [ \fIoptions\fP ...] \fIfile\fP ...] \fIfile\fP
X.SH DESCRIPTION
X\fIMontage\fP creates a composite image by combining several separate images.
The images are tiled on the composite image with the name of the image
appearing just above the individual tile.
X
The composite image is constructed in the following manner.  First,
each image specified on the command line, except for the last, is
scaled to fit the maximum tile size.  The maximum tile size by default
is 256x256.  It can be modified with the \fB-geometry\fP command line
argument or X resource.  See \fBOPTIONS\fP for more information on
command line arguments. See \fBX(1)\fP for more information on X
resources.  Note that the maximum tile size need not be a square.  The
original aspect ratio of each image is maintainted.
X
Next the composite image is initialized with the color specified by the
X\fP-bordercolor\fP command line argument or X resource.  The width and
height of the composite image is determined by the maximum tile size,
the number of tiles per row, the border width and height, and the label
height.  The number of tiles per row specifies how many images are to
appear in each row of the composite image.  The default is to have an
equal number of images in each row and column of the composite.  This
value can be specified with \fB-tiles_per_row\fP.  The border width and
height defaults to the value of the X resource \fB-borderwidth\fP.  It
can be changed with the \fB-borderwidth\fP or \fB-geometry\fP command
line argument or X resource.  The label height is determined by the
font you specify with the \fB-font\fP command line argument or X
resource.  If you do not specify a font, a font is choosen that allows
the name of the image to fit the maximum width of a tiled area.  The
label colors is determined by the \fB-background\fP and
X\fB-foreground\fP command line argument or X resource.  Note, that if
the background and foreground colors are the same, labels will not
appear.
X
XFinally, each image is set onto the composite image with its name
centered just above it.  The individual images are centered within the
width of the tiled area.  The final argument on the command line is the
name assigned to the composite image.  The image is written in the
X\fBMIFF\fP format and may by viewed or printed with \fBdisplay(1)\fP.
See \fBMIFF FILE FORMAT\fP for details on the \fBMIFF\fP format.
X.SH EXAMPLES
To create a montage of a cockatoo, a parrot, and a hummingbird and write
it to a file called birds, use:
X.PP
X     montage cockatoo.miff parrot.miff hummingbird.miff birds.miff
X.PP
To tile several bird images so that they are at most 256 pixels in width and
X192 pixels in height surrounded by a red border 10 pixels wide, use:
X.PP
X     montage -geometry 256x192+10+10 -bordercolor red birds.* montage.miff
X.SH OPTIONS
X.TP 5
X.B "-clip \fI<width>x<height>+<x offset>+<y offset>\fP"
preferred size and location of the clipped image.  See \fBX(1)\fP for details 
about the geometry specification.
X
Use clipping to tile only a particular area of an image.  
X.TP 5
X.B "-colors \fIvalue\fP"
preferred number of colors in the image.  
X
The actual number of colors in the image may be less than your request,
but never more.  Note, this is a color reduction option.  Images with
less unique colors than specified with this option will remain unchanged.
Refer to \fBCOLOR REDUCTION ALGORITHM\fP for more details.
X
Note, options \fB-dither\fP and \fB-treedepth\fP affect the color reduction
algorithm.
X.TP 5
X.B "-compress \fItype\fP"
the type of image compression: \fIQEncoded\fP or \fIRunlengthEncoded\fP.
X
This option specifies the type of image compression for the composite
image.  See \fBMIFF FILE FORMAT\fP for details.
X
Specify \fB\+compress\fP to store the binary image in an uncompressed format.
The default is the compression type of the specified image file.
X.TP 5
X.B "-display \fIhost:display[.screen]\fP"
specifies the X server to contact; see \fBX(1)\fP.
X
Specify \fB+display\fP if an X server is not available.  The label font
is obtained from the X server.  If none is available, the composite image
will not have labels.
X.TP 5
X.B "-dither"
apply Floyd/Steinberg error diffusion to the image.  
X
The basic strategy of dithering is to trade intensity resolution for 
spatial resolution by averaging the intensities of several neighboring 
pixels.  Images which suffer from severe contouring when reducing colors 
can be improved with this option.
X
The \fB-colors\fP, \fB-gray\fP, or \fB-monochrome\fP option is required 
for this option to take effect.
X.TP 5
X.B "-gamma \fIvalue\fP"
level of gamma correction.  
X
The same color image displayed on two different workstations may look
different due to differences in the display monitor.  Use gamma
correction to adjust for this color difference.  Reasonable values
extend from 0.8 to 2.3.  
X.TP 5
X.B "-geometry \fI<width>x<height>+<border width>+<border height>\fP"
preferred tile and border size of each tile of the composite image.  
See \fBX(1)\fP for details about the geometry specification.  By default, 
the tile size is 256x256 and there is no border.
X
The tile size you specify is a maximum size.  Each image is scaled to
fit the maximum size while still retaining its original aspect ratio.
XEach image is surrounded by a border whose size in pixels is specified
as \fBborder width\fP and \fBborder height\fP.
X.TP 5
X.B "-gray"
transform the image to gray scale colors.  
X.TP 5
X.B "-monochrome"
transform the image to black and white.
X
Monochrome images can benefit from error diffusion.  Use \fB-dither\fP with
this option to diffuse the error.
X.TP 5
X.B "-reflect"
create a "mirror image" by reflecting the image scanlines.
X.TP 5
X.B "-rotate \fIdegrees\fP"
apply Paeth image rotation to the image.
X.TP 5
X.B "-tiles_per_row \fIvalue\fP"
specifies how many images are to appear in each row of the composite image.
The default is to have an equal number of images in each row and column of
the composite.
X.TP 5
X.B "-treedepth \fIvalue\fP"
Normally, this integer value is zero or one.  A zero or one tells
X\fIMontage\fP to choose a optimal tree depth for the color reduction
algorithm.  
X
An optimal depth generally allows the best representation of the source
image with the fastest computational speed and the least amount of
memory.  However, the default depth is inappropriate for some images.
To assure the best representation, try values between 2 and 8 for this
parameter.  Refer to \fBCOLOR REDUCTION ALGORITHM\fP for more details.
X
The \fB-colors\fP, \fB-gray\fP, or \fB-monochrome\fP option is required
for this option to take effect.
X.PP
In addition to those listed above, you can specify these standard X
resources as command line options:  -background, -bordercolor, -borderwidth,  
X-font, or -foreground.
See \fBX RESOURCES\fP for details.
X.PP
Any option you specify on the command line remains in effect until it is
explicitly changed by specifying the option again with a different effect.
XFor example, to montage two images, the first with 32 colors and the
second with only 16 colors, use:
X.PP
X     montage -colors 32 cockatoo.1 -colors 16 cockatoo.2 cockatoo.miff
X.PP
Change \fI-\fP to \fI\+\fP in any option above to reverse its effect.
XFor example, specify \fB\+montage\fP to apply image transformations
without viewing them on the X server.  Or, specify \fB\+dither\fP to not
apply error diffusion to an image.
X.PP
Specify \fIfile\fP as \fI-\fP for standard input or output.  If \fIfile\fP 
has the extension \fB.Z\fP, the file is decoded with \fBuncompress\fP.  
X.SH "X RESOURCES"
X\fIMontage\fP options can appear on the command line or in your X
resource file.  Options on the command line supercede values specified
in your X resource file.  See \fBX(1)\fP for more information on X
resources.
X
All \fImontage\fP options have a corresponding X resource.  In addition,
the \fImontage\fP program uses the following X resources:
X.TP 5
X.B background (\fPclass\fB Background)
Specifies the preferred color to use for the composite image background.  The
default is black.
X.TP 5
X.B borderColor (\fPclass\fB BorderColor)
Specifies the preferred color to use for the composite image border.  The
default is white.
X.TP 5
X.B borderWidth (\fPclass\fB BorderWidth)
Specifies the width in pixels of the composite image border.  The default is 2.
X.TP 5
X.B font (\fPclass\fB Font)
Specifies the name of the preferred font to use when displaying text 
within the composite image.  The default is 9x15, fixed, or 5x8 determined by
the composite image size.
X.TP 5
X.B foreground (\fPclass\fB Foreground)
Specifies the preferred color to use for text within the composite image.  The
default is white.
X.SH "COLOR REDUCTION ALGORITHM"
X.PP
This section describes how \fIMontage\fP performs color reduction in an
image.  To fully understand this section, you should have a knowledge
of basic imaging techniques and the tree data structure and terminology.
X.PP
XFor purposes of color allocation, an image is a set of \fIn\fP pixels,
where each pixel is a point in RGB space.  RGB space is a 3-dimensional
vector space, and each pixel, \fIp\d\s-3i\s0\u\fP,  is defined by an
ordered triple of red, green, and blue coordinates, (\fIr\d\s-3i\s0\u,
g\d\s-3i\s0\u, b\d\s-3i\s0\u\fP).
X.PP
XEach primary color component (red, green, or blue) represents an
intensity which varies linearly from 0 to a maximum value,
X\fIc\d\s-3max\s0\u\fP, which corresponds to full saturation of that
color.  Color allocation is defined over a domain consisting of the
cube in RGB space with opposite vertices at (0,0,0) and
X(\fIc\d\s-3max\s0\u,c\d\s-3max\s0\u,c\d\s-3max\s0\u\fP).  \fIMontage\fP
requires \fIc\d\s-3max\s0\u = 255\fP.
X.PP
The algorithm maps this domain onto a tree in which each node
represents a cube within that domain.  In the following discussion,
these cubes are defined by the coordinate of two opposite vertices: The
vertex nearest the origin in RGB space and the vertex farthest from the
origin.
X.PP
The tree's root node represents the the entire domain, (0,0,0) through
X(\fIc\d\s-3max\s0\u,c\d\s-3max\s0\u,c\d\s-3max\s0\u\fP).  Each lower level in
the tree is generated by subdividing one node's cube into eight smaller
cubes of equal size.  This corresponds to bisecting the parent cube
with planes passing through the midpoints of each edge.
X.PP
The basic algorithm operates in three phases:  \fBClassification,
Reduction\fP, and \fBAssignment\fP.  \fBClassification\fP builds a
color description tree for the image.  \fBReduction\fP collapses the
tree until the number it represents, at most, is the number of colors
desired in the output image.  \fBAssignment\fP defines the output
image's color map and sets each pixel's color by reclassification in
the reduced tree.
X.PP
X\fBClassification\fP begins by initializing a color description tree of
sufficient depth to represent each possible input color in a leaf.
However, it is impractical to generate a fully-formed color description
tree in the classification phase for realistic values of
X\fIc\d\s-3max\s0\u\fP.  If color components in the input image are
quantized to \fIk\fP-bit precision, so that \fIc\d\s-3max\s0\u =
X2\u\s-3k\s0\d-1\fP, the tree would need \fIk\fP levels below the root
node to allow representing each possible input color in a leaf.  This
becomes prohibitive because the tree's total number of nodes is
X.PP
X        \fI\s+6\(*S\u\s-9 k\d\di=1\s0 8k\fP\s0\u
X.PP
A complete tree would require 19,173,961 nodes for \fIk = 8,
c\d\s-3max\s0\u = 255\fP.  Therefore, to avoid building a fully
populated tree, \fIMontage\fP: (1) Initializes data structures for
nodes only as they are needed; (2) Chooses a maximum depth for the tree
as a function of the desired number of colors in the output image
X(currently \fIlog\d\s-34\s0\u(colormap size)\+2\fP).  A tree of this
depth generally allows the best representation of the source image with
the fastest computational speed and the least amount of memory.
However, the default depth is inappropriate for some images.
Therefore, the caller can request a specific tree depth.
X.PP
XFor each pixel in the input image, classification scans downward from
the root of the color description tree.  At each level of the tree, it
identifies the single node which represents a cube in RGB space
containing the pixel's color.  It updates the following data for each
such node:
X.TP 5
X.B n\d\s-31\s0\u:  
Number of pixels whose color is contained in the RGB cube which this
node represents;
X.TP 5
X.B n\d\s-32\s0\u:  
Number of pixels whose color is not represented in a node at lower
depth in the tree;  initially,  \fIn\d\s-32\s0\u = 0\fP for all nodes
except leaves of the tree.
X.TP 5
X.B S\d\s-3r\s0\u, S\d\s-3g\s0\u, S\d\s-3b\s0\u:
Sums of the red, green, and blue component values for all pixels not
classified at a lower depth.  The combination of these sums and
X\fIn\d\s-32\s0\u\fP will ultimately characterize the mean color of a
set of pixels represented by this node.
X.PP
X\fBReduction\fP repeatedly prunes the tree until the number of nodes with
X\fIn\d\s-32\s0\u  > 0\fP is less than or equal to the maximum number of colors
allowed in the output image.  On any given iteration over the tree, it
selects those nodes whose \fIn\d\s-31\s0\u\fP count is minimal for pruning and
merges their color statistics upward.  It uses a pruning threshold,
X\fIn\d\s-3p\s0\u\fP, to govern node selection as follows:
X.PP
X  n\d\s-3p\s0\u = 0
X  while number of nodes with (n\d\s-32\s0\u > 0) > required maximum number of colors
X      prune all nodes such that n\d\s-31\s0\u <= n\d\s-3p\s0\u
X      Set n\d\s-3p\s0\u  to minimum n\d\s-31\s0\u  in remaining nodes
X.PP
When a node to be pruned has offspring, the pruning procedure invokes
itself recursively in order to prune the tree from the leaves upward.
The values of \fIn\d\s-32\s0\u  S\d\s-3r\s0\u, S\d\s-3g\s0\u,\fP  and
X\fIS\d\s-3b\s0\u\fP in a node being pruned are always added to the
corresponding data in that node's parent.  This retains the pruned
node's color characteristics for later averaging.
X.PP
XFor each node,  \fIn\d\s-32\s0\u\fP pixels exist for which that node
represents the smallest volume in RGB space containing those pixel's
colors.  When \fIn\d\s-32\s0\u  > 0\fP the node will uniquely define a
color in the output image.  At the beginning of reduction,
X\fIn\d\s-32\s0\u = 0\fP  for all nodes except the leaves of the tree
which represent colors present in the input image.
X.PP
The other pixel count, \fIn\d\s-31\s0\u\fP,  indicates the total
number of colors within the cubic volume which the node represents.
This includes \fIn\d\s-31\s0\u - n\d\s-32\s0\u\fP pixels whose colors
should be defined by nodes at a lower level in the tree.
X.PP
X\fBAssignment\fP generates the output image from the pruned tree.  The
output image consists of two parts:  (1)  A color map, which is an
array of color descriptions (RGB triples) for each color present in the
output image; (2)  A pixel array, which represents each pixel as an
index into the color map array.
X.PP
XFirst, the assignment phase makes one pass over the pruned color
description tree to establish the image's color map.  For each node
with \fIn\d\s-32\s0\u > 0\fP, it divides \fIS\d\s-3r\s0\u,
S\d\s-3g\s0\u\fP, and \fPS\d\s-3b\s0\u\fP by \fIn\d\s-32\s0\u\fP.  This
produces the mean color of all pixels that classify no lower than this
node.  Each of these colors becomes an entry in the color map.
X.PP
XFinally, the assignment phase reclassifies each pixel in the pruned
tree to identify the deepest node containing the pixel's color.  The
pixel's value in the pixel array becomes the index of this node's mean
color in the color map.
X.SH "MEASURING COLOR REDUCTION ERROR"
X.PP
Depending on the image, the color reduction error may be obvious or
invisible.  Images with high spatial frequencies (such as hair or
grass) will show error much less than pictures with large smoothly
shaded areas (such as faces).  This is because the high-frequency
contour edges introduced by the color reduction process are masked by
the high frequencies in the image.
X.PP
To measure the difference between the original and color reduced images
X(the total color reduction error), \fIMontage\fP sums over all pixels
in an image the distance squared in RGB space between each original
pixel value and its color reduced value. \fIMontage\fP prints several error 
measurements including the mean error per pixel, the normalized mean error,
and the normalized maximum error.
X.PP
The normalized error measurement can be used to compare images.  In
general, the closer the mean error is to zero the more the quantized
image resembles the source image.  Ideally, the error should be
perceptually-based, since the human eye is the final judge of
quantization quality.
X.PP
These errors are measured and printed when \fB-verbose\fP and \fB-colors\fI 
are specified on the command line:
X.TP 5
X.B mean error per pixel:  
is the mean error for any single pixel in the image.
X.TP 5
X.B normalized mean square error:  
is the normalized mean square quantization error for any single pixel in the
image.  
X
This distance measure is normalized to a range between 0 and 1.  It is
independent of the range of red, green, and blue values in the image.
X.TP 5
X.B normalized maximum square error:  
is the largest normalized square quantization error for any single
pixel in the image.
X
This distance measure is normalized to a range between 0 and 1.  It is
independent of the range of red, green, and blue values in the image.
X.SH "MIFF FILE FORMAT"
X.PP
The Machine Independent File Format is described in this section.
X.PP
A MIFF image file consist of two sections.  The first section is
composed of keywords describing the image in text form.  The next
section is the binary image data.  The two sections are separated by a
X\fB:\fP character immediately followed by a \fInewline\fP.  Generally,
the first section has a \fIform-feed\fP and \fInewline\fP proceeding
the \fB:\fP character.   You can then list the image keywords with
X\fImore\fP, without printing the binary image that follows the \fB:\fP
separator.
X.PP
XEach keyword must be separated by at least one space but can be
separated with control characters such a \fIform-feed\fP or
X\fInewline\fP.
X.PP
A list of valid keywords follows:
X.TP 5
X.B "class=\fIDirectClass | PseudoClass\fP"
identifies the type of binary image stored within the file.  
X
This keyword is optional.  If it is not specified, a \fIDirectClass\fP
image format is assumed.  An explanation of \fIDirectClass\fP and
X\fIPseudoClass\fP image data follows this list.
X.TP 5
X.B "colors=\fIvalue\fP"
specifies the number of colors in the image, and for pseudo-color
images the size of the colormap.  
X
This keyword is optional.  However, if a colormap size is not
specified, a linear colormap is assumed for pseudo-color images.
X.TP 5
X.B "columns=\fIvalue\fP"
is a required keyword and specifies the number of columns, or width in
pixels, of the image.
X.TP 5
X.B "compression=\fIQEncoded | RunlengthEncoded\fP"
identifies how the image stored within the file is compressed.
X
This keyword is optional.  If it is not specified, the image is assumed
to be uncompressed.  A detailed explanation of runlength-encoded and
Q-coder image compression follows this list.
X.TP 5
X.B "id=\fIImageMagick\fP"
is a required keyword and identifies this file as a MIFF image.  
X.TP 5
X.B "packets=\fIvalue\fP"
specifies the number of compressed color packets in the image data section.  
X
This keyword is optional, but recommended, for runlength-encoded image
compression.  It is required for Q-encoded image compression.  A
detailed explanation of image compression follows this list.
X.TP 5
X.B "rows=\fIvalue\fP"
is a required keyword and specifies the number of rows, or height in pixels, 
of the image.
X.TP 5
X.B "scene=\fIvalue\fP"
is an optional keyword and is a reference number for sequencing of
images.  
X
This keyword is typically useful for animating a sequence of images.
X.PP
Comments can be included in the keyword section.  Comments must begin with
a \fB{\fP character and end with a \fI}\fP character.  
X.PP
An example keyword section follows:
X.PP
X    {
X      Rendered via Dore by Sandy Hause.
X    }
X    id=ImageMagick
X    class=PseudoClass  colors=256
X    compression=RunlengthEncoded  packets=27601
X    columns=1280  rows=1024
X    scene=1
X    ^L
X    :
X.PP
The binary image data that follows the keyword text is stored in one of 
two binary classes as specified by the \fBclass\fP keyword: 
X\fIDirectClass\fP or \fIPseudoClass\fP.
X.PP
Use the \fIDirectClass\fP class to store continuous-tone images.
X\fIDirectClass\fP requires that the image pixels immediately follow the
keyword text and be stored as binary red, green, and blue intensity
values.  The total number of pixels expected is equal to the number of pixel 
columns times the number of pixel rows as specified by the \fBcolumns\fP and 
X\fBrows\fP keywords.
X.PP
If the \fBcompression\fP keyword is not specified, a red, green, and blue byte 
in that order is expected for each pixel of the image.
X.PP
If \fBcompression\fP is \fIQEncoded\fP, each red, green, and blue byte
intensity value is encoded using the Q-coder compression algorithm.
Use the \fBpackets\fP keyword to specify the total number of Q-encoded
packets that comprise the image.  Refer to "Sofware implementations of
the Q-Coder", by Mitchell, J. L. and Pennebaker, W.B. (IBM Journal Res.
Development, Volume 32, Number 6, November 1988, pages 753 - 774) for
implementation specific details.
X.PP
If \fBcompression\fP is \fIRunlengthEncoded\fP, each red, green, and
blue byte intensity value is followed by a count byte. This value
specifies the number of horizonally contiguous pixels in the image of
that color.  The count (0-255) is one less than the actual number of
contiguous pixels; thus a single packet can represent from 1 up to 256
identical pixels.  The total number of pixels specified by the
individual count bytes must add up to the number of pixel columns times
the number of pixel rows as specified by the \fBcolumns\fP and
X\fBrows\fP keywords.  Use \fBpackets\fP to specify the total number of
runlength-encoded packets that comprise the image.
X.PP
Use the \fIPseudoClass\fP class to store pseudo-color images.
X\fIPseudoClass\fP requires that the image colormap and
pseudo-color pixels immediately follow the keyword text.  The colormap
is stored as contiguous red, green, and blue intensity values.  The
number of intensity values expected is determined by the \fBcolors\fP
keyword.  Note, an image colormap is restricted to at most 65535
entries.  The binary pseudo-color image is stored as indexes into the
colormap.  If the colormap size exceeds 256 entries, then each colormap
index is two bytes each with the most-significant-byte first.  The
total number of pixels expected is equal to the number of pixel columns
times the number of pixel rows as specified by the \fBcolumns\fP and
X\fBrows\fP keywords.
X.PP
If the \fBcompression\fP keyword is not specified, a colormap index is 
expected for each pixel of the image.
X.PP
If \fBcompression\fP is \fIQEncoded\fP, each colormap index is
encoded using the Q-coder compression algorithm.  Use the \fBpackets\fP
keyword to specify the total number of Q-encoded packets comprise the
image.  Refer to "Sofware implementations of the Q-Coder", by Mitchell,
J. L. and Pennebaker, W.B. (IBM Journal Res. Development, Volume 32,
Number 6, November 1988, pages 753 - 774) for implementation specific
details.
X.PP
If \fBcompression\fP is \fIRunlengthEncoded\fP, each colormap index
is followed by a count byte. This value  specifies the number of
horizonally contiguous pixels in the image of that color.  The count
X(0-255) is one less than the actual number of contiguous pixels; thus a
single packet can represent from 1 up to 256 identical pixels.  The
total number of pixels specified by the individual count bytes must add
up to the number of pixels expected in the image as specified by the
X\fBcolumns\fP and \fBrows\fP keywords.  Use \fBpackets\fP to specify the 
total number of runlength-encoded packets that comprise the image.
X.SH FEATURES
X\fIMontage\fP memory requirements is proportionate to the area of the image.
X.PP
X\fIMontage\fP does not complain when it encounters a keyword in an image file
it does not understand.
X.SH ENVIRONMENT
X.TP 5
X.B DISPLAY
To get the default host, display number, and screen.
X.SH SEE ALSO
XX(1), display(1), more(1), compress(1)
X.SH COPYRIGHT
Copyright 1991 E. I. Dupont de Nemours & Company                           
X.PP                                                                           
Permission to use, copy, modify, distribute, and sell this software and    
its documentation for any purpose is hereby granted without fee,           
provided that the above copyright notice appear in all copies and that     
both that copyright notice and this permission notice appear in            
supporting documentation, and that the name of E. I. Dupont de Nemours     
X& Company not be used in advertising or publicity pertaining to            
distribution of the software without specific, written prior               
permission.  E. I. Dupont de Nemours & Company makes no representations    
about the suitability of this software for any purpose.  It is provided    
X"as is" without express or implied warranty.                               
X.PP
XE. I. Dupont de Nemours & Company disclaims all warranties with regard
to this software, including all implied warranties of merchantability
and fitness, in no event shall E. I. Dupont de Nemours & Company be
liable for any special, indirect or consequential damages or any
damages whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action, arising
out of or in connection with the use or performance of this software.      
X.SH ACKNOWLEDGEMENTS
The MIT X Consortium for making network transparent graphics a reality.
X.PP
Michael Halle, Spatial Imaging Group at MIT, for the initial
implementation of Alan Paeth's image rotation algorithm.
X.PP
David Pensak, E. I. Dupont de Nemours & Company, for providing a
computing environment that made this program possible.
X.PP
Paul Raveling, USC Information Sciences Institute, for the original
idea of using space subdivision for the color reduction algorithm.
With Paul's permission, the \fBCOLOR REDUCTION ALGORITHM\fP section is
a adaptation from a document he wrote.
X.SH AUTHORS
John Cristy, E.I. DuPont de Nemours & Company Incorporated
END_OF_FILE
if test 26687 -ne `wc -c <'montage.man'`; then
    echo shar: \"'montage.man'\" unpacked with wrong size!
fi
chmod +x 'montage.man'
# end of 'montage.man'
fi
if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Imakefile'\"
else
echo shar: Extracting \"'Imakefile'\" \(1157 characters\)
sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
XEXTRA_LIBRARIES= $(XLIB) -lm
PROGRAMS= display import XtoPS animate montage
SRCS= display.c X.c image.c kolb.c quantize.c colors.c rotate.c compress.c\
X  PreR4Icccm.c
OBJS= display.o X.o image.o kolb.o quantize.o colors.o rotate.o compress.o\
X  PreR4Icccm.o
ImportObjects= import.o X.o image.o kolb.o quantize.o rotate.o compress.o\
X  PreR4Icccm.o
XXtoPSObjects= XtoPS.o X.o image.o kolb.o quantize.o rotate.o compress.o\
X  PreR4Icccm.o
AnimateObjects= animate.o X.o image.o kolb.o quantize.o colors.o rotate.o\
X  compress.o PreR4Icccm.o
MontageObjects= montage.o X.o image.o kolb.o quantize.o colors.o rotate.o\
X  compress.o PreR4Icccm.o
X
AllTarget($(PROGRAMS))
X
ComplexProgramTarget(display)
SingleProgramTarget(import,$(ImportObjects), , )
InstallProgram(import,$(BINDIR))
InstallManPage(import,$(MANDIR))
SingleProgramTarget(XtoPS,$(XtoPSObjects), , )
InstallProgram(XtoPS,$(BINDIR))
InstallManPage(XtoPS,$(MANDIR))
SingleProgramTarget(animate,$(AnimateObjects), , )
InstallProgram(animate,$(BINDIR))
InstallManPage(animate,$(MANDIR))
SingleProgramTarget(montage,$(MontageObjects), , )
InstallProgram(montage,$(BINDIR))
InstallManPage(montage,$(MANDIR))
END_OF_FILE
if test 1157 -ne `wc -c <'Imakefile'`; then
    echo shar: \"'Imakefile'\" unpacked with wrong size!
fi
chmod +x 'Imakefile'
# end of 'Imakefile'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(10599 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile generated by imake - do not edit!
X# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
X#
X# The cpp used on this machine replaces all newlines and multiple tabs and
X# spaces in a macro expansion with a single space.  Imake tries to compensate
X# for this, but is not always successful.
X#
X
X###########################################################################
X# Makefile generated from "Imake.tmpl" and <Imakefile>
X# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
X#
X# Platform-specific parameters may be set in the appropriate .cf
X# configuration files.  Site-wide parameters may be set in the file
X# site.def.  Full rebuilds are recommended if any parameters are changed.
X#
X# If your C preprocessor doesn't define any unique symbols, you'll need
X# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
X# "make Makefile", "make Makefiles", or "make World").
X#
X# If you absolutely can't get imake to work, you'll need to set the
X# variables at the top of each Makefile as well as the dependencies at the
X# bottom (makedepend will do this automatically).
X#
X
X###########################################################################
X# platform-specific configuration parameters - edit sun.cf to change
X
X# platform:  $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
X# operating system:  SunOS 4.0.3
X
X###########################################################################
X# site-specific configuration parameters - edit site.def to change
X
X# site:  $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $
X
X            SHELL = /bin/sh
X
X              TOP = .
X      CURRENT_DIR = .
X
X               AR = ar cq
X  BOOTSTRAPCFLAGS =
X               CC = cc
X
X         COMPRESS = compress
X              CPP = /lib/cpp $(STD_CPP_DEFINES)
X    PREPROCESSCMD = cc -E $(STD_CPP_DEFINES)
X          INSTALL = install
X               LD = ld
X             LINT = lint
X      LINTLIBFLAG = -C
X         LINTOPTS = -axz
X               LN = ln -s
X             MAKE = make
X               MV = mv
X               CP = cp
X           RANLIB = ranlib
X  RANLIBINSTFLAGS =
X               RM = rm -f
X     STD_INCLUDES =
X  STD_CPP_DEFINES =
X      STD_DEFINES =
X EXTRA_LOAD_FLAGS =
X  EXTRA_LIBRARIES =
X             TAGS = ctags
X
X    SHAREDCODEDEF = -DSHAREDCODE
X         SHLIBDEF = -DSUNSHLIB
X
X    PROTO_DEFINES =
X
X     INSTPGMFLAGS =
X
X     INSTBINFLAGS = -m 0755
X     INSTUIDFLAGS = -m 4755
X     INSTLIBFLAGS = -m 0664
X     INSTINCFLAGS = -m 0444
X     INSTMANFLAGS = -m 0444
X     INSTDATFLAGS = -m 0444
X    INSTKMEMFLAGS = -m 4755
X
X          DESTDIR =
X
X     TOP_INCLUDES = -I$(INCROOT)
X
X      CDEBUGFLAGS = -O
X        CCOPTIONS =
X      COMPATFLAGS =
X
X      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
X       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
X           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
X        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
X           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
X        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
X   LDCOMBINEFLAGS = -X -r
X
X        MACROFILE = sun.cf
X           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
X
X    IMAKE_DEFINES =
X
X         IRULESRC = $(CONFIGDIR)
X        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
X
X     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
X			$(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
X			$(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
X
X###########################################################################
X# X Window System Build Parameters
X# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
X
X###########################################################################
X# X Window System make variables; this need to be coordinated with rules
X# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
X
X          PATHSEP = /
X        USRLIBDIR = $(DESTDIR)/usr/lib
X           BINDIR = $(DESTDIR)/usr/bin/X11
X          INCROOT = $(DESTDIR)/usr/include
X     BUILDINCROOT = $(TOP)
X      BUILDINCDIR = $(BUILDINCROOT)/X11
X      BUILDINCTOP = ..
X           INCDIR = $(INCROOT)/X11
X           ADMDIR = $(DESTDIR)/usr/adm
X           LIBDIR = $(USRLIBDIR)/X11
X        CONFIGDIR = $(LIBDIR)/config
X       LINTLIBDIR = $(USRLIBDIR)/lint
X
X          FONTDIR = $(LIBDIR)/fonts
X         XINITDIR = $(LIBDIR)/xinit
X           XDMDIR = $(LIBDIR)/xdm
X           AWMDIR = $(LIBDIR)/awm
X           TWMDIR = $(LIBDIR)/twm
X           GWMDIR = $(LIBDIR)/gwm
X          MANPATH = $(DESTDIR)/usr/man
X    MANSOURCEPATH = $(MANPATH)/man
X           MANDIR = $(MANSOURCEPATH)n
X        LIBMANDIR = $(MANSOURCEPATH)3
X      XAPPLOADDIR = $(LIBDIR)/app-defaults
X
X        SOXLIBREV = 4.2
X          SOXTREV = 4.0
X         SOXAWREV = 4.0
X        SOOLDXREV = 4.0
X         SOXMUREV = 4.0
X        SOXEXTREV = 4.0
X
X       FONTCFLAGS = -t
X
X     INSTAPPFLAGS = $(INSTDATFLAGS)
X
X            IMAKE = imake
X           DEPEND = makedepend
X              RGB = rgb
X            FONTC = bdftosnf
X        MKFONTDIR = mkfontdir
X        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
X
X        CONFIGSRC = $(TOP)/config
X        CLIENTSRC = $(TOP)/clients
X          DEMOSRC = $(TOP)/demos
X           LIBSRC = $(TOP)/lib
X          FONTSRC = $(TOP)/fonts
X       INCLUDESRC = $(TOP)/X11
X        SERVERSRC = $(TOP)/server
X          UTILSRC = $(TOP)/util
X        SCRIPTSRC = $(UTILSRC)/scripts
X       EXAMPLESRC = $(TOP)/examples
X       CONTRIBSRC = $(TOP)/../contrib
X           DOCSRC = $(TOP)/doc
X           RGBSRC = $(TOP)/rgb
X        DEPENDSRC = $(UTILSRC)/makedepend
X         IMAKESRC = $(CONFIGSRC)
X         XAUTHSRC = $(LIBSRC)/Xau
X          XLIBSRC = $(LIBSRC)/X
X           XMUSRC = $(LIBSRC)/Xmu
X       TOOLKITSRC = $(LIBSRC)/Xt
X       AWIDGETSRC = $(LIBSRC)/Xaw
X       OLDXLIBSRC = $(LIBSRC)/oldX
X      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
X      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
X     MKFONTDIRSRC = $(FONTSRC)/mkfontdir
X     EXTENSIONSRC = $(TOP)/extensions
X
X  DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a
X     EXTENSIONLIB =  -lXext
X
X          DEPXLIB = $(DEPEXTENSIONLIB)
X             XLIB = $(EXTENSIONLIB) -lX11
X
X      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
X         XAUTHLIB =  -lXau
X
X        DEPXMULIB =
X           XMULIB = -lXmu
X
X       DEPOLDXLIB =
X          OLDXLIB = -loldX
X
X      DEPXTOOLLIB =
X         XTOOLLIB = -lXt
X
X        DEPXAWLIB =
X           XAWLIB = -lXaw
X
X LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
X         LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
X          LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
X        LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
X          LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
X
X          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
X
X         DEPLIBS1 = $(DEPLIBS)
X         DEPLIBS2 = $(DEPLIBS)
X         DEPLIBS3 = $(DEPLIBS)
X
X###########################################################################
X# Imake rules for building libraries, programs, scripts, and data files
X# rules:  $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
X
X###########################################################################
X# start of Imakefile
X
XEXTRA_LIBRARIES= $(XLIB) -lm
PROGRAMS= display import XtoPS animate montage
SRCS= display.c X.c image.c kolb.c quantize.c colors.c rotate.c compress.c\
X  PreR4Icccm.c
OBJS= display.o X.o image.o kolb.o quantize.o colors.o rotate.o compress.o\
X  PreR4Icccm.o
ImportObjects= import.o X.o image.o kolb.o quantize.o rotate.o compress.o\
X  PreR4Icccm.o
XXtoPSObjects= XtoPS.o X.o image.o kolb.o quantize.o rotate.o compress.o\
X  PreR4Icccm.o
AnimateObjects= animate.o X.o image.o kolb.o quantize.o colors.o rotate.o\
X  compress.o PreR4Icccm.o
MontageObjects= montage.o X.o image.o kolb.o quantize.o colors.o rotate.o\
X  compress.o PreR4Icccm.o
X
all:: $(PROGRAMS)
X
X PROGRAM = display
X
all:: display
X
display: $(OBJS) $(DEPLIBS)
X	$(RM) $@
X	$(CC) -o $@ $(OBJS) $(LDOPTIONS) $(LOCAL_LIBRARIES) $(LDLIBS) $(EXTRA_LOAD_FLAGS)
X
saber_display:
X	#load $(ALLDEFINES) $(SRCS) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
X
osaber_display:
X	#load $(ALLDEFINES) $(OBJS) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
X
install:: display
X	$(INSTALL) -c $(INSTPGMFLAGS)   display $(BINDIR)
X
install.man:: display.man
X	$(INSTALL) -c $(INSTMANFLAGS) display.man $(MANDIR)/display.n
X
depend::
X	$(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(SRCS)
X
lint:
X	$(LINT) $(LINTFLAGS) $(SRCS) $(LINTLIBS)
lint1:
X	$(LINT) $(LINTFLAGS) $(FILE) $(LINTLIBS)
X
clean::
X	$(RM) $(PROGRAM)
X
import: $(ImportObjects)
X	$(RM) $@
X	$(CC) -o $@ $(ImportObjects) $(LDOPTIONS)   $(LDLIBS)   $(EXTRA_LOAD_FLAGS)
X
clean::
X	$(RM) import
X
install:: import
X	$(INSTALL) -c $(INSTPGMFLAGS)   import $(BINDIR)
X
install.man:: import.man
X	$(INSTALL) -c $(INSTMANFLAGS) import.man $(MANDIR)/import.n
X
XXtoPS: $(XtoPSObjects)
X	$(RM) $@
X	$(CC) -o $@ $(XtoPSObjects) $(LDOPTIONS)   $(LDLIBS)   $(EXTRA_LOAD_FLAGS)
X
clean::
X	$(RM) XtoPS
X
install:: XtoPS
X	$(INSTALL) -c $(INSTPGMFLAGS)   XtoPS $(BINDIR)
X
install.man:: XtoPS.man
X	$(INSTALL) -c $(INSTMANFLAGS) XtoPS.man $(MANDIR)/XtoPS.n
X
animate: $(AnimateObjects)
X	$(RM) $@
X	$(CC) -o $@ $(AnimateObjects) $(LDOPTIONS)   $(LDLIBS)   $(EXTRA_LOAD_FLAGS)
X
clean::
X	$(RM) animate
X
install:: animate
X	$(INSTALL) -c $(INSTPGMFLAGS)   animate $(BINDIR)
X
install.man:: animate.man
X	$(INSTALL) -c $(INSTMANFLAGS) animate.man $(MANDIR)/animate.n
X
montage: $(MontageObjects)
X	$(RM) $@
X	$(CC) -o $@ $(MontageObjects) $(LDOPTIONS)   $(LDLIBS)   $(EXTRA_LOAD_FLAGS)
X
clean::
X	$(RM) montage
X
install:: montage
X	$(INSTALL) -c $(INSTPGMFLAGS)   montage $(BINDIR)
X
install.man:: montage.man
X	$(INSTALL) -c $(INSTMANFLAGS) montage.man $(MANDIR)/montage.n
X
X###########################################################################
X# common rules for all Makefiles - do not edit
X
emptyrule::
X
clean::
X	$(RM_CMD) \#*
X
Makefile::
X	-@if [ -f Makefile ]; then \
X	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
X	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
X	else exit 0; fi
X	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
X
tags::
X	$(TAGS) -w *.[ch]
X	$(TAGS) -xw *.[ch] > TAGS
X
saber:
X	#load $(ALLDEFINES) $(SRCS)
X
osaber:
X	#load $(ALLDEFINES) $(OBJS)
X
X###########################################################################
X# empty rules for directories that do not have SUBDIRS - do not edit
X
install::
X	@echo "install in $(CURRENT_DIR) done"
X
install.man::
X	@echo "install.man in $(CURRENT_DIR) done"
X
Makefiles::
X
includes::
X
X###########################################################################
X# dependencies generated by makedepend
X
END_OF_FILE
if test 10599 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
chmod +x 'Makefile'
# end of 'Makefile'
fi
if test -f 'montage.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'montage.c'\"
else
echo shar: Extracting \"'montage.c'\" \(40269 characters\)
sed "s/^X//" >'montage.c' <<'END_OF_FILE'
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%             M   M   OOO   N   N  TTTTT   AAA    GGG   EEEEE                 %
X%             MM MM  O   O  NN  N    T    A   A  G      E                     %
X%             M M M  O   O  N N N    T    AAAAA  G GG   EEE                   %
X%             M   M  O   O  N  NN    T    A   A  G  G   E                     %
X%             M   M   OOO   N   N    T    A   A  GGGG   EEEEE                 %
X%                                                                             %
X%                                                                             %
X%          Montage Machine Independent File Format Image via X11.             %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%                           Software Design                                   %
X%                             John Cristy                                     %
X%                            January  1991                                    %
X%                                                                             %
X%                                                                             %
X%  Copyright 1991 E. I. Dupont de Nemours & Company                           %
X%                                                                             %
X%  Permission to use, copy, modify, distribute, and sell this software and    %
X%  its documentation for any purpose is hereby granted without fee,           %
X%  provided that the above Copyright notice appear in all copies and that     %
X%  both that Copyright notice and this permission notice appear in            %
X%  supporting documentation, and that the name of E. I. Dupont de Nemours     %
X%  & Company not be used in advertising or publicity pertaining to            %
X%  distribution of the software without specific, written prior               %
X%  permission.  E. I. Dupont de Nemours & Company makes no representations    %
X%  about the suitability of this software for any purpose.  It is provided    %
X%  "as is" without express or implied warranty.                               %
X%                                                                             %
X%  E. I. Dupont de Nemours & Company disclaims all warranties with regard     %
X%  to this software, including all implied warranties of merchantability      %
X%  and fitness, in no event shall E. I. Dupont de Nemours & Company be        %
X%  liable for any special, indirect or consequential damages or any           %
X%  damages whatsoever resulting from loss of use, data or profits, whether    %
X%  in an action of contract, negligence or other tortious action, arising     %
X%  out of or in connection with the use or performance of this software.      %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Montage creates a composite image by combining several separate
X%  images.  The images are tiled on the composite image with the name of
X%  the image appearing just above the individual tile.
X%  
X%  The composite image is constructed in the following manner.  First,
X%  each image specified on the command line, except for the last, is
X%  scaled to fit the maximum tile size.  The maximum tile size by default
X%  is 256x256.  It can be modified with the -geometry command line
X%  argument or X resource.  Note that the maximum tile size need not be a
X%  square.  The original aspect ratio of each image is maintainted.
X%  
X%  Next the composite image is initialized with the color specified by the
X%  -bordercolor command line argument or X resource.  The width and height
X%  of the composite image is determined by the maximum tile size, the
X%  number of tiles per row, the border width and height, and the label
X%  height.  The number of tiles per row specifies how many images are to
X%  appear in each row of the composite image.  The default is to have an
X%  equal number of images in each row and column of the composite.  This
X%  value can be specified with -tiles_per_row.  The border width and
X%  height defaults to the value of the X resource -borderwidth.  It can be
X%  changed with the -borderwidth or -geometry command line argument or X
X%  resource.  The label height is determined by the font you specify with
X%  the -font command line argument or X resource.  If you do not specify a
X%  font, a font is choosen that allows the name of the image to fit the
X%  maximum width of a tiled area.  The label colors is determined by the
X%  -background and -foreground command line argument or X resource.  Note,
X%  that if the background and foreground colors are the same, labels will
X%  not appear.
X%  
X%  Finally, each image is set onto the composite image with its name
X%  centered just above it.  The individual images are centered within the
X%  width of the tiled area.  The final argument on the command line is the
X%  name assigned to the composite image.  The image is written in the MIFF
X%  format and may by viewed or printed with display.  
X%
X%  The Montage program command syntax is:
X%
X%  Usage: montage [options ...] file [ [options ...] file ...] file
X%
X%  Where options include:
X%    -clip geometry       preferred size and location of the clipped image
X%    -colors value        preferred number of colors in the image
X%    -compress type       compress image: RunlengthEncoded or QEncoded
X%    -display server      query fonts from this X server
X%    -dither              apply Floyd/Steinberg error diffusion to image
X%    -gamma value         level of gamma correction
X%    -geometry geometry   preferred tile and border sizes
X%    -gray                transform image to gray scale colors
X%    -monochrome          transform image to black and white
X%    -reflect             reverse image scanlines
X%    -rotate degrees      apply Paeth rotation to the image
X%    -tiles_per_row value number of image tiles per row
X%    -treedepth value     depth of the color classification tree
X%    -verbose             print detailed information about the image
X%
X%  In addition to those listed above, you can specify these standard X
X%  resources as command line options:  -background, -bordercolor -borderwidth, 
X%  -font, or -foreground.
X%
X%  Change '-' to '+' in any option above to reverse its effect.  For
X%  example, specify +compress to store the image as uncompressed.
X%
X%  Specify 'file' as '-' for standard input or output.
X%
X%
X*/
X
X/*
X  Include declarations.
X*/
X#include "display.h"
X#include "image.h"
X#include "X.h"
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   C o m p r e s s I m a g e                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function CompressImage compresses an image to the minimum number of
X%  runlength-encoded packets.
X%
X%  The format of the CompressImage routine is:
X%
X%      CompressImage(image)
X%
X%  A description of each parameter follows:
X%
X%    o image: The address of a structure of type Image.
X%
X%
X*/
static void CompressImage(image)
Image
X  *image;
X{
X  register int
X    i;
X
X  register RunlengthPacket
X    *p,
X    *q;
X
X  /*
X    Compress image.
X  */
X  p=image->pixels;
X  image->packets=0;
X  q=image->pixels;
X  q->length=MaxRunlength;
X  for (i=0; i < (image->columns*image->rows); i++)
X  {
X    if ((p->red == q->red) && (p->green == q->green) &&
X        (p->blue == q->blue) && (q->length < MaxRunlength))
X       q->length++;
X     else
X       {
X         if (image->packets > 0)
X           q++;
X         image->packets++;
X         q->red=p->red;
X         q->green=p->green;
X         q->blue=p->blue;
X         q->index=p->index;
X         q->length=0;
X       }
X    p++;
X  }
X  image->pixels=(RunlengthPacket *)
X    realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   E r r o r                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function Error displays an error message and then terminates the program.
X%
X%  The format of the Error routine is:
X%
X%      Error(message,qualifier)
X%
X%  A description of each parameter follows:
X%
X%    o message: Specifies the message to display before terminating the
X%      program.
X%
X%    o qualifier: Specifies any qualifier to the message.
X%
X%
X*/
void Error(message,qualifier)
char
X  *message,
X  *qualifier;
X{
X  (void) fprintf(stderr,"%s: %s",application_name,message);
X  if (qualifier != (char *) NULL)
X    (void) fprintf(stderr," (%s)",qualifier);
X  (void) fprintf(stderr,".\n");
X  exit(1);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   M o n t a g e I m a g e                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function MontageImage creates a composite image by combining several
X%  separate images.
X%
X%  The format of the MontageImage routine is:
X%
X%      MontageImage(display,resource_info,tiles_per_row,image,number_tiles)
X%
X%  A description of each parameter follows:
X%
X%    o display: Specifies a connection to an X server;  returned from
X%      XOpenDisplay.
X%
X%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
X%
X%    o tiles_per_row: Specifies the number of arguments.
X%
X%    o image: Specifies a pointer to a Image structure; returned from
X%      ReadImage.
X%
X%    o number_tiles: Specifies the number of tiles to tile.
X%
X%
X*/
static int LinearCompare(image_1,image_2)
Image
X  **image_1,
X  **image_2;
X{
X  return((int) (*image_1)->scene-(int) (*image_2)->scene);
X}
X
static Image *MontageImage(display,resource_info,tiles_per_row,images,
X  number_tiles)
Display
X  *display;
X
XXResourceInfo
X  *resource_info;
X
unsigned int
X  tiles_per_row;
X
Image
X  **images;
X
unsigned int
X  number_tiles;
X{
X  char
X    text[256];
X
X  GC
X    graphic_context;
X
X  Image
X    *montage_image;
X
X  int
X    tile_border_width,
X    tile_border_height;
X
X  Pixmap
X    label;
X
X  register int
X    i,
X    x,
X    y;
X
X  register RunlengthPacket
X    *p,
X    *q;
X
X  unsigned int
X    label_height,
X    label_width,
X    tile,
X    tile_height,
X    tile_width,
X    x_offset,
X    y_offset;
X
X  Window
X    root_window;
X
X  XColor
X    background_color,
X    border_color,
X    foreground_color;
X
X  XFontStruct
X    *font_info;
X
X  XGCValues
X    graphic_context_value;
X
X
X  XImage
X    *label_image;
X
X  XVisualInfo
X    *visual_info;
X
X  /*
X    Determine tile sizes.
X  */
X  tile_border_width=resource_info->border_width;
X  tile_border_height=resource_info->border_width;
X  tile_width=256;
X  tile_height=256;
X  if (resource_info->image_geometry != (char *) NULL)
X    XParseGeometry(resource_info->image_geometry,&tile_border_width,
X      &tile_border_height,&tile_width,&tile_height);
X  if (tiles_per_row == 0)
X    tiles_per_row=(unsigned int) sqrt((double) number_tiles);
X  if (!display)
X    {
X      /*
X        No X server-- no labels.
X      */
X      label_height=0;
X      border_color.red=0;
X      border_color.green=0;
X      border_color.blue=0;
X    }
X  else 
X    {
X      /*
X        Initialize visual info.
X      */
X      visual_info=XBestVisualInfo(display,"default",(char *) NULL,
X        (XStandardColormap *) NULL);
X      if (visual_info == (XVisualInfo *) NULL)
X         Error("unable to get visual",resource_info->visual_type);
X      /*
X        Initialize font info.
X      */
X      tile=0;
X      for (i=1; i < number_tiles; i++)
X        if (strlen(images[i]->filename) > strlen(images[tile]->filename))
X          tile=i;
X      (void) sprintf(text," %s \0",images[tile]->filename);
X      font_info=XBestFont(display,resource_info,text,tile_width);
X      if (font_info == (XFontStruct *) NULL)
X        Error("unable to load font",resource_info->font_name);
X      /*
X        Determine label sizes.
X      */
X      root_window=XRootWindow(display,XDefaultScreen(display));
X      label_width=XTextWidth(font_info,text,strlen(text));
X      label_height=font_info->ascent+font_info->descent;
X      label=XCreatePixmap(display,root_window,label_width,label_height,
X        visual_info->depth);
X      /*
X        Initialize graphics info.
X      */
X      graphic_context_value.background=0;
X      graphic_context_value.foreground=1;
X      graphic_context_value.font=font_info->fid;
X      graphic_context=XCreateGC(display,root_window,GCBackground | GCFont | 
X        GCForeground,&graphic_context_value);
X      if (graphic_context == (GC) NULL)
X        Error("unable to create graphic context",(char *) NULL);
X      /*
X        Determine background, border, and foreground colors.
X      */
X      XParseColor(display,XDefaultColormap(display,visual_info->screen),
X        resource_info->background_color,&background_color);
X      XParseColor(display,XDefaultColormap(display,visual_info->screen),
X        resource_info->border_color,&border_color);
X      XParseColor(display,XDefaultColormap(display,visual_info->screen),
X        resource_info->foreground_color,&foreground_color);
X    }
X  /*
X    Allocate image structure.
X  */
X  montage_image=(Image *) malloc(sizeof(Image));
X  if (montage_image == (Image *) NULL)
X    Error("memory allocation error",(char *) NULL);
X  /*
X    Initialize Image structure.
X  */
X  montage_image->class=DirectClass;
X  montage_image->compression=RunlengthEncodedCompression;
X  montage_image->columns=(tile_width+tile_border_width*2)*tiles_per_row;
X  montage_image->rows=(tile_height+tile_border_height*2+label_height)*
X    (number_tiles/tiles_per_row+((number_tiles % tiles_per_row) != 0))+
X    (tile_border_height >> 1);
X  montage_image->packets=montage_image->columns*montage_image->rows;
X  montage_image->colors=0;
X  montage_image->scene=0;
X  montage_image->colormap=(ColorPacket *) NULL;
X  montage_image->pixels=(RunlengthPacket *) NULL;
X  montage_image->comments=(char *) NULL;
X  montage_image->pixels=(RunlengthPacket *)
X    malloc(montage_image->packets*sizeof(RunlengthPacket));
X  if (montage_image->pixels == (RunlengthPacket *) NULL)
X    Error("memory allocation error",(char *) NULL);
X  /*
X    Initialize montage image to border color.
X  */
X  p=montage_image->pixels;
X  for (i=0; i < montage_image->packets; i++)
X  {
X    p->red=border_color.red >> 8;
X    p->green=border_color.green >> 8;
X    p->blue=border_color.blue >> 8;
X    p->index=0;
X    p->length=0;
X    p++;
X  }
X  /*
X    Sort images by increasing tile number.
X  */
X  qsort((char *) images,(int) number_tiles,sizeof(Image *),LinearCompare);
X  /*
X    Copy tile images to the composite image.
X  */
X  for (tile=0; tile < number_tiles; tile++)
X  {
X    x_offset=(tile_width+tile_border_width*2)*(tile % tiles_per_row)+
X      tile_border_width;
X    y_offset=(tile_height+tile_border_width*2)*(tile/tiles_per_row)+
X      (tile_border_height >> 1);
X    if (display)
X      {
X        /*
X          Copy tile label to the composite image.
X        */
X        (void) sprintf(text," %s \0",images[tile]->filename);
X        XDrawImageString(display,label,graphic_context,0,font_info->ascent,
X          text,strlen(text));
X        label_width=XTextWidth(font_info,text,strlen(text));
X        label_image=XGetImage(display,label,0,0,label_width,label_height,
X          AllPlanes,ZPixmap);
X        y_offset+=label_height*(tile/tiles_per_row);
X        q=montage_image->pixels+(y_offset*montage_image->columns)+x_offset+
X          ((tile_width-label_width) >> 1);
X        for (y=0; y < label_height; y++)
X        {
X          for (x=0; x < label_width; x++)
X          {
X            if (XGetPixel(label_image,x,y) == 1)
X              {
X                q->red=foreground_color.red >> 8;
X                q->green=foreground_color.green >> 8;
X                q->blue=foreground_color.blue >> 8;
X              }
X            else
X              {
X                q->red=background_color.red >> 8;
X                q->green=background_color.green >> 8;
X                q->blue=background_color.blue >> 8;
X              }
X            q++;
X          }
X          q+=montage_image->columns-label_width;
X        }
X        XDestroyImage(label_image);
X      }
X    /*
X      Copy this tile to the composite image.
X    */
X    p=images[tile]->pixels;
X    images[tile]->runlength=p->length+1;
X    y_offset+=tile_border_height+label_height;
X    q=montage_image->pixels+(y_offset*montage_image->columns)+x_offset+
X      ((tile_width-images[tile]->columns) >> 1);
X    for (y=0; y < images[tile]->rows; y++)
X    {
X      for (x=0; x < images[tile]->columns; x++)
X      {
X        if (images[tile]->runlength > 0)
X          images[tile]->runlength--;
X        else
X          {
X            p++;
X            images[tile]->runlength=p->length;
X          }
X        q->red=p->red;
X        q->green=p->green;
X        q->blue=p->blue;
X        q++;
X      }
X      q+=montage_image->columns-images[tile]->columns;
X    }
X    DestroyImage(images[tile]);
X  }
X  if (display)
X    {
X      /*
X        Free X resources.
X      */
X      XFreePixmap(display,label);
X      XFreeGC(display,graphic_context);
X      XFreeFont(display,font_info);
X      XFree((char *) visual_info);
X    }
X  /*
X    Compress image.
X  */
X  CompressImage(montage_image);
X  return(montage_image);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   U s a g e                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function Usage displays the program command syntax.
X%
X%  The format of the Usage routine is:
X%
X%      Usage(message,terminate)
X%
X%  A description of each parameter follows:
X%
X%    o message: Specifies a specific message to display to the user.
X%
X%    o terminate: The program will exit if the value is not zero.
X%
X%
X*/
static void Usage(message,terminate)
char
X  *message;
X
unsigned int
X  terminate;
X{
X  char
X    **p;
X
X  static char
X    *options[]=
X    {
X      "-clip geometry       preferred size and location of the clipped image",
X      "-colors value        preferred number of colors in the image",
X      "-compress type       compress image: RunlengthEncoded or QEncoded",
X      "-display server      query font from this X server",
X      "-dither              apply Floyd/Steinberg error diffusion to image",
X      "-gamma value         level of gamma correction",
X      "-geometry geometry   preferred size and location of the image window",
X      "-gray                transform image to gray scale colors",
X      "-monochrome          transform image to black and white",
X      "-reflect             reflect the image scanlines",
X      "-rotate degrees      apply Paeth rotation to the image",
X      "-tiles_per_row value number of image tiles per row",
X      "-treedepth value     depth of the color classification tree",
X      "-verbose             print detailed information about the image",
X      (char *) NULL
X    };
X  if (message != (char *) NULL)
X    (void) fprintf(stderr,"Can't continue, %s\n\n",message);
X  (void) fprintf(stderr,
X    "Usage: %s [-options ...] file [ [-options ...] file ...] file\n",
X    application_name);
X  (void) fprintf(stderr,"\nWhere options include: \n");
X  for (p=options; *p != (char *) NULL; p++)
X    (void) fprintf(stderr,"  %s\n",*p);
X  (void) fprintf(stderr,
X    "\nIn addition to those listed above, you can specify these standard X\n");
X  (void) fprintf(stderr,
X    "resources as command line options:  -background, -bordercolor,\n");
X  (void) fprintf(stderr,
X    "-borderwidth, -font, or -foreground\n");
X  (void) fprintf(stderr,
X    "\nChange '-' to '+' in any option above to reverse its effect.  For\n");
X  (void) fprintf(stderr,
X    "example, specify +compress to store the image as uncompressed.\n");
X  (void) fprintf(stderr,
X    "\nSpecify 'file' as '-' for standard input or output.\n");
X  if (terminate)
X    exit(1);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%    M a i n                                                                  %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
int main(argc,argv)
int
X  argc;
X
char
X  **argv;
X{
X  char
X    *clip_geometry,
X    *option,
X    *server_name;
X
X  Display
X    *display;
X
X  double
X    gamma;
X
X  Image
X    **images,
X    *montage_image;
X
X  int
X    degrees;
X
X  register int
X    i;
X
X  unsigned int
X    compression,
X    display_image,
X    dither,
X    gray,
X    image_number,
X    number_colors,
X    reflect,
X    tiles_per_row,
X    tree_depth,
X    verbose;
X
X  XResourceInfo
X    resource_info;
X
X  /*
X    Display usage profile if there are no command line arguments.
X  */
X  application_name=(*argv);
X  if (argc < 3)
X    Usage((char *) NULL,True);
X  /*
X    Set defaults.
X  */
X  clip_geometry=(char *) NULL;
X  compression=UnknownCompression;
X  degrees=0;
X  display=(Display *) NULL;
X  display_image=True;
X  dither=False;
X  gamma=0.0;
X  gray=False;
X  number_colors=0;
X  reflect=False;
X  resource_info.border_width=0;
X  resource_info.image_geometry=(char *) NULL;
X  resource_info.monochrome=False;
X  server_name=(char *) NULL;
X  tiles_per_row=0;
X  tree_depth=0;
X  verbose=False;
X  images=(Image **) malloc((unsigned int) argc*sizeof(Image *));
X  if (images == (Image **) NULL)
X    Error("unable to animate images","memory allocation failed");
X  /*
X    Check for server name specified on the command line.
X  */
X  for (i=1; i < argc; i++)
X  {
X    /*
X      Check command line for server name.
X    */
X    option=argv[i];
X    if ((strlen(option) > 1) && ((*option == '-') || (*option == '+')))
X      if (strncmp("display",option+1,3) == 0)
X        {
X          /*
X            User specified server name.
X          */
X          display_image=(*option == '-');
X          if (display_image)
X            {
X              i++;
X              if (i == argc)
X                Usage("missing server name on -display",True);
X              server_name=argv[i];
X            }
X          break;
X        }
X  }
X  if (display_image)
X    {
X      char
X        *user_default;
X
X      /*
X        Open X server connection.
X      */
X      display=XOpenDisplay(server_name);
X      if (display == (Display *) NULL)
X        Error("unable to connect to X server",XDisplayName(server_name));
X      /*
X        Get user_defaults from X resource database.
X      */
X      user_default=XGetDefault(display,application_name,"background");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"Background");
X      if (user_default == (char *) NULL)
X        user_default="black";
X      resource_info.background_color=user_default;
X      user_default=XGetDefault(display,application_name,"borderColor");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"BorderColor");
X      if (user_default == (char *) NULL)
X        user_default="black";
X      resource_info.border_color=user_default;
X      user_default=XGetDefault(display,application_name,"borderWidth");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"BorderWidth");
X      resource_info.border_width=
X        (user_default == (char *) NULL) ? 2 : atoi(user_default);
X      clip_geometry=XGetDefault(display,application_name,"clip");
X      user_default=XGetDefault(display,application_name,"colors");
X      number_colors=user_default ? atoi(user_default) : 0;
X      user_default=XGetDefault(display,application_name,"compression");
X      if (user_default == (char *) NULL)
X        compression=UnknownCompression;
X      else
X        if (*user_default == 'R')
X          compression=RunlengthEncodedCompression;
X        else
X          if (*user_default == 'Q')
X            compression=QEncodedCompression;
X          else
X            compression=UnknownCompression;
X      user_default=XGetDefault(display,application_name,"dither");
X      dither=IsTrue(user_default);
X      user_default=XGetDefault(display,application_name,"font");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"Font");
X      resource_info.font_name=user_default;
X      user_default=XGetDefault(display,application_name,"foreground");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"Foreground");
X      if (user_default == (char *) NULL)
X        user_default="white";
X      resource_info.foreground_color=user_default;
X      user_default=XGetDefault(display,application_name,"gamma");
X      gamma=(user_default == (char *) NULL) ? 0.0 : atof(user_default);
X      user_default=XGetDefault(display,application_name,"geometry");
X      if (user_default == (char *) NULL)
X        user_default=XGetDefault(display,application_name,"Geometry");
X      resource_info.image_geometry=user_default;
X      user_default=XGetDefault(display,application_name,"gray");
X      gray=IsTrue(user_default);
X      user_default=XGetDefault(display,application_name,"monochrome");
X      resource_info.monochrome=IsTrue(user_default);
X      user_default=XGetDefault(display,application_name,"reflect");
X      reflect=IsTrue(user_default);
X      user_default=XGetDefault(display,application_name,"rotate");
X      degrees=(user_default == (char *) NULL) ? 0 : atoi(user_default);
X      user_default=XGetDefault(display,application_name,"tiles_per_row");
X      tiles_per_row=(user_default == (char *) NULL) ? 0 : atoi(user_default);
X      user_default=XGetDefault(display,application_name,"treedepth");
X      tree_depth=(user_default == (char *) NULL) ? 0 : atoi(user_default);
X      user_default=XGetDefault(display,application_name,"verbose");
X      verbose=IsTrue(user_default);
X    }
X  /*
X    Parse command line.
X  */
X  image_number=0;
X  for (i=1; i < (argc-1); i++)
X  {
X    option=argv[i];
X    if ((strlen(option) > 1) && ((*option == '-') || (*option == '+')))
X      switch (*(option+1))
X      {
X        case 'b':
X        {
X          if (strncmp("background",option+1,5) == 0)
X            {
X              resource_info.background_color=(char *) NULL;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing color on -background",True);
X                  resource_info.background_color=argv[i];
X                }
X            }
X          else
X            if (strncmp("bordercolor",option+1,7) == 0)
X              {
X                resource_info.border_color=(char *) NULL;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing color on -bordercolor",True);
X                    resource_info.border_color=argv[i];
X                  }
X              }
X            else
X              if (strncmp("borderwidth",option+1,7) == 0)
X                {
X                  resource_info.border_width=0;
X                  if (*option == '-')
X                    {
X                      i++;
X                      if (i == argc)
X                        Usage("missing width on -borderwidth",True);
X                      resource_info.border_width=atoi(argv[i]);
X                    }
X                }
X              else
X                Usage(option,True);
X          break;
X        }
X        case 'c':
X        {
X          if (strncmp("clip",option+1,2) == 0)
X            {
X              clip_geometry=(char *) NULL;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing geometry on -clip",True);
X                  clip_geometry=argv[i];
X                }
X            }
X          else
X            if (strncmp("colors",option+1,3) == 0)
X              {
X                number_colors=0;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing colors on -colors",True);
X                    number_colors=atoi(argv[i]);
X                  }
X              }
X            else
X              if (strncmp("compress",option+1,3) == 0)
X                {
X                  compression=NoCompression;
X                  if (*option == '-')
X                    {
X                      i++;
X                      if (i == argc)
X                        Usage("missing type on -compress",True);
X                      if ((*argv[i] == 'R') || (*argv[i] == 'r'))
X                        compression=RunlengthEncodedCompression;
X                      else
X                        if ((*argv[i] == 'Q') || (*argv[i] == 'q'))
X                          compression=QEncodedCompression;
X                        else
X                          Usage("invalid compression type on -compress",True);
X                    }
X                }
X              else
X                Usage(option,True);
X          break;
X        }
X        case 'd':
X        {
X          if (strncmp("display",option+1,3) == 0)
X            {
X              server_name=(char *) NULL;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing server name on -display",True);
X                  server_name=argv[i];
X                }
X            }
X          else
X            if (strncmp("dither",option+1,3) == 0)
X              dither=(*option == '-');
X            else
X              Usage(option,True);
X          break;
X        }
X        case 'f':
X        {
X          if (strncmp("font",option+1,3) == 0)
X            {
X              resource_info.font_name=(char *) NULL;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing font name on -font",True);
X                  resource_info.font_name=argv[i];
X                }
X            }
X          else
X            if (strncmp("foreground",option+1,3) == 0)
X              {
X                resource_info.foreground_color=(char *) NULL;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing foreground on -foreground",True);
X                    resource_info.foreground_color=argv[i];
X                  }
X              }
X            else
X              Usage(option,True);
X          break;
X        }
X        case 'g':
X        {
X          if (strncmp("gamma",option+1,2) == 0)
X            {
X              gamma=0.0;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing gamma on -gamma",True);
X                  gamma=atof(argv[i]);
X                }
X            }
X          else
X            if (strncmp("geometry",option+1,2) == 0)
X              {
X                resource_info.image_geometry=(char *) NULL;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing geometry on -geometry",True);
X                    resource_info.image_geometry=argv[i];
X                  }
X              }
X            else
X              if (strncmp("gray",option+1,2) == 0)
X                gray=(*option == '-');
X              else
X                Usage(option,True);
X          break;
X        }
X        case 'h':
X        {
X          Usage((char *) NULL,True);
X          break;
X        }
X        case 'm':
X        {
X          resource_info.monochrome=(*option == '-');
X          break;
X        }
X        case 'r':
X        {
X          if (strncmp("reflect",option+1,2) == 0)
X            reflect=(*option == '-');
X          else
X            if (strncmp("rotate",option+1,3) == 0)
X              {
X                degrees=0.0;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing degrees on -rotate",True);
X                    degrees=atoi(argv[i]);
X                  }
X              }
X            else
X              Usage(option,True);
X          break;
X        }
X        case 't':
X        {
X          if (strncmp("tiles_per_row",option+1,2) == 0)
X            {
X              tiles_per_row=0;
X              if (*option == '-')
X                {
X                  i++;
X                  if (i == argc)
X                    Usage("missing value on -tiles_per_row",True);
X                  tree_depth=atoi(argv[i]);
X                }
X            }
X          else
X            if (strncmp("treedepth",option+1,2) == 0)
X              {
X                tree_depth=0;
X                if (*option == '-')
X                  {
X                    i++;
X                    if (i == argc)
X                      Usage("missing depth on -treedepth",True);
X                    tree_depth=atoi(argv[i]);
X                  }
X              }
X            else
X              Usage(option,True);
X          break;
X        }
X        case 'v':
X        {
X          verbose=(*option == '-');
X          break;
X        }
X        default:
X        {
X          Usage((char *) NULL,True);
X          break;
X        }
X      }
X    else
X      {
X        char
X          geometry[256];
X
X        Image
X          *image,
X          info_image;
X
X        unsigned int
X          tile_height,
X          tile_width;
X
X        /*
X          Option is a file name: begin by reading image from specified file.
X        */
X        image=ReadImage(option);
X        if (image == (Image *) NULL)
X          continue;
X        if (image->scene == 0)
X          image->scene=image_number;
X        info_image=(*image);
X        /*
X          Transform image as defined by the image geometry
X        */
X        tile_width=256;
X        tile_height=256;
X        if (resource_info.image_geometry != (char *) NULL)
X          {
X            int
X              x,
X              y;
X
X            XParseGeometry(resource_info.image_geometry,&x,&y,&tile_width,
X              &tile_height);
X          }
X        if (image->columns >= image->rows)
X          tile_height=
X            (tile_height*((image->rows << 14)/image->columns)+8191) >> 14;
X        else
X          tile_width=
X            (tile_width*((image->columns << 14)/image->rows)+8191) >> 14;
X        (void) sprintf(geometry,"%dx%d\0",tile_width,tile_height);
X        image=TransformImage(image,clip_geometry,geometry,(char *) NULL);
X        if (reflect)
X          {
X            Image
X              *reflected_image;
X
X            /*
X              Reverse image scanlines.
X            */
X            reflected_image=ReflectImage(image);
X            if (reflected_image != (Image *) NULL)
X              {
X                DestroyImage(image);
X                image=reflected_image;
X              }
X          }
X        if ((degrees % 360) != 0)
X          {
X            Image
X              *rotated_image;
X
X            /*
X              Rotate image.
X            */
X            rotated_image=RotateImage(image,(double) degrees,False);
X            if (rotated_image != (Image *) NULL)
X              {
X                DestroyImage(image);
X                image=rotated_image;
X              }
X          }
X        if (gamma > 0.0)
X          (void) GammaImage(image,gamma);
X        if (gray)
X          {
X            /*
X              Convert image to gray scale PseudoColor class.
X            */
X            (void) GrayImage(image);
X            if (image->class == DirectClass)
X              QuantizeImage(image,256,tree_depth,dither,True);
X          }
X        if (resource_info.monochrome)
X          {
X            register unsigned int
X              bit;
X
X            /*
X              Convert image to resource_info.monochrome PseudoColor class.
X            */
X            (void) GrayImage(image);
X            (void) QuantizeImage(image,2,tree_depth,dither,True);
X            bit=Intensity(image->colormap[0]) > Intensity(image->colormap[1]);
X            image->colormap[bit].red=0;
X            image->colormap[bit].green=0;
X            image->colormap[bit].blue=0;
X            image->colormap[!bit].red=255;
X            image->colormap[!bit].green=255;
X            image->colormap[!bit].blue=255;
X          }
X        if (verbose)
X          {
X            /*
X              Display detailed info about the image.
X            */
X            (void) fprintf(stderr,"[%d] %s",(image->scene == 0 ? image_number :
X              image->scene),image->filename);
X            (void) fprintf(stderr," %dx%d",info_image.columns,info_image.rows);
X            if ((info_image.columns != image->columns) ||
X                (info_image.rows != image->rows))
X              (void) fprintf(stderr,"=>%dx%d",image->columns,image->rows);
X            if (image->class == DirectClass)
X              (void) fprintf(stderr," DirectClass ");
X            else
X              (void) fprintf(stderr," PseudoClass %dc",image->colors);
X            (void) fprintf(stderr,"\n");
X          }
X        images[image_number++]=image;
X      }
X    }
X  if (image_number == 0)
X    Usage("missing an image file name",True);
X  else
X    {
X      montage_image=MontageImage(display,&resource_info,tiles_per_row,images,
X        image_number);
X      if (number_colors > 0)
X        QuantizeImage(montage_image,number_colors,tree_depth,dither,True);
X      if (compression != UnknownCompression)
X        montage_image->compression=compression;
X      strcpy(montage_image->filename,argv[argc-1]);
X      (void) WriteImage(montage_image);
X      (void) free((char *) images);
X    }
X  if (display != (Display *) NULL)
X    XCloseDisplay(display);
X  return(False);
X}
END_OF_FILE
if test 40269 -ne `wc -c <'montage.c'`; then
    echo shar: \"'montage.c'\" unpacked with wrong size!
fi
chmod +x 'montage.c'
# end of 'montage.c'
fi
echo shar: End of shell archive.
exit 0
--
The UUCP Mailer