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