[comp.sources.x] v08i075: xgrabsc -- make images of portions of the screen, Part01/02

bruce@SLC.COM (Bruce Schuchardt) (08/21/90)

Submitted-by: bruce@SLC.COM (Bruce Schuchardt)
Posting-number: Volume 8, Issue 75
Archive-name: xgrabsc/part01

There are a number of programs available for getting X-Windows
screen dumps (e.g., xwd, xwps and DEC's session manager), but they
all either write their output in an unusable format, or are restricted
to dumps of single shell windows.  I wrote xgrabsc in an effort to get
around these restrictions.

The main contribution of this program is its use of root-window
rubberbanding to allow capture of arbitrary portions of the screen,
and its multiple output formats (x-bitmap, x-pixmap, xwd, Postscript
and puzzle formats).

The Postscript dumps will use run-length encoding if it results in
any savings in size.  A typical xterm window dump on a mono system
runs around 40K bytes.  An xwd dump on the same screen will be around
650K bytes.

The program should build easily enough.  If you don't have imake,
use simple.mak (i.e., make -f simple.mak).  For building, you 
should only have to alter the makefiles if your include files or 
libraries are in a non-standard location (/usr/include/X11, /usr/
lib).

The makefiles will install xgrabsc in /usr/bin/X11 and the man page in
/usr/man/man1.  Change the makefile before installing if you don't like
these locations.


Please send comments, bugs and enhancements to bruce@slc.com.


   File Name		Archive #	Description
-----------------------------------------------------------
 Imakefile                  1	        imake makefile
 README                     1	        this stuff
 cpyright.h                 1	        copyright notice
 patchlevel.h               2	        current patch level
 simple.mak                 2	        simple 'make' makefile
 xgrabsc.c                  1	        program source
 xgrabsc.man                1	        man page for xgrabsc

-----------------CUT HERE---------------------
#! /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 archive 1 (of 2)."
# Contents:  README Imakefile cpyright.h xgrabsc.c xgrabsc.man
# Wrapped by owner@spruce on Mon Aug 20 10:03:02 1990
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'\" \(2167 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X*========================================================================
X*
X* Name - README
X*
X* Version:	1.3
X*
X* ccsid:	@(#)README	1.3 - 8/20/90 09:55:04
X* from: 	ccs/s.README
X* date: 	8/20/90 09:55:59
X*
X* Copyright (C) 1990, Bruce Schuchardt
X* See either the man page or the file cpyright.h for full copyright
X* information.
X*  
X* Description:  README for xgrabsc
X*
X*========================================================================
X
XThere are a number of programs available for getting X-Windows
Xscreen dumps (e.g., xwd, xwps and DEC's session manager), but they
Xall either write their output in an unusable format, or are restricted
Xto dumps of single shell windows.  I wrote xgrabsc in an effort to get
Xaround these restrictions.
X
XThe main contribution of this program is its use of root-window
Xrubberbanding to allow capture of arbitrary portions of the screen,
Xand its multiple output formats (x-bitmap, x-pixmap, xwd, Postscript
Xand puzzle formats).
X
XThe Postscript dumps will use run-length encoding if it results in
Xany savings in size.  A typical xterm window dump on a mono system
Xruns around 40K bytes.  An xwd dump on the same screen will be around
X650K bytes.
X
XThe program should build easily enough.  If you don't have imake,
Xuse simple.mak (i.e., make -f simple.mak).  For building, you 
Xshould only have to alter the makefiles if your include files or 
Xlibraries are in a non-standard location (/usr/include/X11, /usr/
Xlib).
X
XThe makefiles will install xgrabsc in /usr/bin/X11 and the man page in
X/usr/man/man1.  Change the makefile before installing if you don't like
Xthese locations.
X
X
XPlease send comments, bugs and enhancements to bruce@slc.com.
X
X
XManifest
X--------
XREADME              this file
X
XImakefile           Input for "imake" program
Xsimple.mak          Simpler input for standard "make"
X
Xcpyright.h          Copyright notice for this software (READ IT)
Xpatchlevel.h        Current patch level
Xxgrabsc.c           Program source
X
Xxgrabsc.man         Man page for xgrabsc
X
X
X
X+----------------------------+
X|     Bruce Schuchardt       |
X|    Servio Corporation      |
X|      bruce@slc.com         |
X+----------------------------+
X
END_OF_FILE
if test 2167 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Imakefile'\"
else
echo shar: Extracting \"'Imakefile'\" \(944 characters\)
sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
X/*========================================================================
X *
X * Name - Imakefile
X *
X * Version:	1.3
X *
X * ccsid:	@(#)Imakefile	1.3 - 8/20/90 09:54:45
X * from: 	ccs/s.Imakefile
X * date: 	8/20/90 09:55:58
X *
X * Description: imake file to build xgrabsc.  Use simple.mak if you
X *              don't have imake.
X *
X *========================================================================
X */
X
X/* change INSTALL_PATH to the directory in which you want xgrabsc installed */
XINSTALL_PATH    = /usr/bin/X11
X
X/* change MAN_PATH to point to your man page top directory */
XMAN_PATH        = /usr/man
X/* change MAN_EXT to the section for xgrabsc */
XMAN_EXT         = 1
X
X/* if you trust your optimizer, change 'g' to 'O' */
XCDEBUGFLAGS     = -g
XLOCAL_LIBRARIES = $(XLIB)
X
XSimpleProgramTarget(xgrabsc)
X
Xinstall::
X	$(INSTALL) -c -s xgrabsc $(INSTALL_PATH)
X	$(INSTALL) -c -m 644 xgrabsc.man \
X		$(MAN_PATH)/man$(MAN_EXT)/xgrabsc.$(MAN_EXT)X
END_OF_FILE
if test 944 -ne `wc -c <'Imakefile'`; then
    echo shar: \"'Imakefile'\" unpacked with wrong size!
fi
# end of 'Imakefile'
fi
if test -f 'cpyright.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpyright.h'\"
else
echo shar: Extracting \"'cpyright.h'\" \(1576 characters\)
sed "s/^X//" >'cpyright.h' <<'END_OF_FILE'
X#ifndef _COPYRIGHT_
X#define _COPYRIGHT_
X/*========================================================================
X*
X* Name - cpyright.h
X*
X* Version:	1.1
X*
X* ccsid:	@(#)cpyright.h	1.1 - 8/16/90 09:12:34
X* from: 	ccs/s.cpyright.h
X* date: 	8/20/90 09:56:00
X*
X* Description: copyright information for xgrabsc
X*
X*========================================================================
X*
X* Dithering and Halftoning code:
X* Copyright 1989, 1990 Jim Frost
X*
X* All other code:
X* Copyright (C) 1990 Bruce Schuchardt
X*
X* Permission to use, copy, modify, distribute, and sell this software
X* and its documentation for any purpose is hereby granted without fee,
X* provided that the above copyright notice appear in all copies and
X* that both that copyright notice and this permission notice appear
X* in supporting documentation.  The author makes no representations
X* about the suitability of this software for any purpose.  It is
X* provided "as is" without express or implied warranty.
X*
X* THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
X* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
X* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
X* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
X* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
X* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
X* USE OR PERFORMANCE OF THIS SOFTWARE.
X*/
X
Xstatic char *XLoadImage_Copyright= "Copyright (C) 1989 Jim Frost";
Xstatic char *Copyright = "Copyright (C) 1990 Bruce Schuchardt";
X#endif
X
END_OF_FILE
if test 1576 -ne `wc -c <'cpyright.h'`; then
    echo shar: \"'cpyright.h'\" unpacked with wrong size!
fi
# end of 'cpyright.h'
fi
if test -f 'xgrabsc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xgrabsc.c'\"
else
echo shar: Extracting \"'xgrabsc.c'\" \(38224 characters\)
sed "s/^X//" >'xgrabsc.c' <<'END_OF_FILE'
X/*========================================================================
X *
X * Name - xgrabsc.c
X *
X * Version:	1.4
X *
X * ccsid:	@(#)xgrabsc.c	1.4 - 8/20/90 09:55:19
X * from: 	ccs/s.xgrabsc.c
X * date: 	8/20/90 09:56:02
X *
X * Copyright (c) 1990 Bruce Schuchardt.
X * Read the file cpyright.h for full copyright information.
X *  
X *
X * Description: 
X *
X * xgrabsc - grab screen images and store in files
X *
X *========================================================================
X */
X
X#include "cpyright.h"
X#include "patchlevel.h"
X
X#include <stdio.h>
X
X#include <X11/Xos.h>
X#include <X11/Xlib.h>
X#include <X11/Xutil.h>
X#include <X11/cursorfont.h>
X#include <X11/Xatom.h>
X#include <X11/XWDFile.h>
X#ifndef CARD32
X#include <X11/Xmd.h>
X#endif
X
X#define MAX_CELLS  256
X#define TRUE  1
X#define FALSE 0
X
Xtypedef unsigned char byte;
Xtypedef unsigned long dw;
Xtypedef unsigned int  word;
X
X
X
Xtypedef struct {
X  XImage *ximage;
X  word numcells;
X  word red[MAX_CELLS], green[MAX_CELLS], blue[MAX_CELLS];
X  byte used[MAX_CELLS];
X} imageInfo;
X
X
XDisplay *hDisplay;
Xint      hScreen;
XWindow   hRoot;
Xint      displayCells;
Xchar    *programName;
Xchar    *imageName;
X
Xint      patchLevel = XGRABSC_PATCHLEVEL;
Xint      verbose;
X
Xword nr[MAX_CELLS], ng[MAX_CELLS], nb[MAX_CELLS];
X
X
X
X
X
X
X/*
X * Alter colors by setting or clearing bits in rgb values.
X * This effectively reduces the depth of the image, causing the
X * number of colors used to be reduced.  Equivalent colors are
X * merged in the image, and the used flags of remapped colors are
X * cleared.
X *
X * The number of eliminated colormap entries is returned.  The colormap
X * is not compressed.
X */
XalterPlanes(image, modeIsAnd, bits)
X  imageInfo *image;
X  int modeIsAnd;      /* if TRUE, combine mask with AND; if FALSE, use OR */
X  unsigned int bits;
X{
X  int nc, cidx, ridx, h, w;
X  long p;
X  XImage *ximage = image->ximage;
X  long map[MAX_CELLS];
X  int remapCount;
X  word mask;
X  
X  if (ximage->depth <= 1)
X    return 0;
X
X  mask = 0xFFFF ^ ((1 << (bits+8)) - 1);
X  if (!modeIsAnd)
X    mask = ~mask & 0xFFFF;
X
X  if (verbose) {
X    fprintf(stderr, "%s: %s color with mask %x...", programName,
X            modeIsAnd? "ANDing" : "ORing", mask);
X    fflush(stderr);
X  }
X
X  nc = image->numcells;
X  if (modeIsAnd)
X    for (cidx=0; cidx<nc; cidx++) {
X      nr[cidx] = image->red[cidx]   & mask;
X      ng[cidx] = image->green[cidx] & mask;
X      nb[cidx] = image->blue[cidx]  & mask;
X    }
X  else
X    for (cidx=0; cidx<nc; cidx++) {
X      nr[cidx] = image->red[cidx]   | mask;
X      ng[cidx] = image->green[cidx] | mask;
X      nb[cidx] = image->blue[cidx]  | mask;
X    }
X
X  /* now eliminate redundant colors */
X  for (cidx=0; cidx<nc; cidx++)
X    map[cidx] = cidx;
X  remapCount = 0;
X  for (cidx=0; cidx<nc; cidx++)
X    if (image->used[cidx])
X      for (ridx=cidx+1; ridx<nc; ridx++)
X        if (image->used[ridx]  &&
X            nr[cidx]==nr[ridx] &&
X            ng[cidx]==ng[ridx] &&
X            nb[cidx]==nb[ridx]) {
X          /* the colors match - remap this pixel to the one we're scanning with */
X          map[ridx] = cidx;
X          image->used[ridx] = FALSE;
X          remapCount++;
X        }
X
X  memcpy(image->red,   nr, nc*sizeof(word));
X  memcpy(image->green, ng, nc*sizeof(word));
X  memcpy(image->blue,  nb, nc*sizeof(word));
X
X  /* remap redundant pixels in the image */
X  if (remapCount)
X    for (h=0; h<ximage->height; h++)
X      for (w=0; w<ximage->width; w++) {
X        p = XGetPixel(ximage, w, h);
X        if (p != map[p])
X          XPutPixel(ximage, w, h, map[p]);
X      }
X
X  if (verbose)
X    fprintf(stderr, "  %d colors remapped\n", remapCount, nc);
X  return remapCount;
X}
X
X
X
X
X
X/* Brighten or darken colors in the image by the given amount ('percent').
X * The amount is an integer that, if less than 100 will darken the image
X * and if greater than 100 will brighten the image.  After modifying
X * colors equivalent colors are merged (as in alterPlanes).  The number
X * of eliminated colors is returned.
X */
XbrightenColors(image, percent)
X  imageInfo *image;
X  int percent;
X{
X  int nc, cidx, ridx, h, w;
X  long p;
X  XImage *ximage = image->ximage;
X  float  adjustment;
X  long map[MAX_CELLS];
X  int remapCount;
X  dw new;
X
X  if (ximage->depth <= 1)
X    return 0;
X
X  if (verbose) {
X    fprintf(stderr, "%s: adjusting intensity by %d...", programName, percent);
X    fflush(stderr);
X  }
X
X  adjustment = (float)percent / 100.0;
X  nc = image->numcells;
X  for (cidx=0; cidx<nc; cidx++) {
X    new = image->red[cidx] * adjustment;
X    if (new > (dw)0xFFFF) new = (dw)0xFFFF;
X    nr[cidx] = new;
X    new = image->green[cidx] * adjustment;
X    if (new > (dw)0xFFFF) new = (dw)0xFFFF;
X    ng[cidx] = new;
X    new = image->blue[cidx] * adjustment;
X    if (new > (dw)0xFFFF) new = (dw)0xFFFF;
X    nb[cidx] = new;
X  }
X
X  /* now eliminate redundant colors */
X  for (cidx=0; cidx<nc; cidx++)
X    map[cidx] = cidx;
X  remapCount = 0;
X  for (cidx=0; cidx<nc; cidx++)
X    if (image->used[cidx])
X      for (ridx=cidx+1; ridx<nc; ridx++)
X        if (image->used[ridx]  &&
X            nr[cidx]==nr[ridx] &&
X            ng[cidx]==ng[ridx] &&
X            nb[cidx]==nb[ridx]) {
X          map[ridx] = cidx;
X          image->used[ridx] = FALSE;
X          remapCount++;
X        }
X
X  memcpy(image->red,   nr, nc*sizeof(word));
X  memcpy(image->green, ng, nc*sizeof(word));
X  memcpy(image->blue,  nb, nc*sizeof(word));
X
X  /* remap redundant pixels in the image */
X  if (remapCount)
X    for (h=0; h<ximage->height; h++)
X      for (w=0; w<ximage->width; w++) {
X        p = XGetPixel(ximage, w, h);
X        if (p != map[p])
X          XPutPixel(ximage, w, h, map[p]);
X      }
X
X
X  if (verbose)
X    fprintf(stderr, "  %d colors remapped\n", remapCount, nc);
X
X  return remapCount;
X}
X
X
X
X
X
X/*
X * Compress the colors used in an XImage so that all pixel values are
X * adjacent.  Alters the rgb color tables and the XImage data values.
X */
XcompressColormap(image)
X  imageInfo *image;
X{
X  XImage *ximage = image->ximage;
X  long map[MAX_CELLS];
X  int  ncolors, w, h, m;
X  long p;
X  
X  if (ximage->depth <= 1  ||  image->numcells > MAX_CELLS)
X    return;
X
X  if (verbose) {
X    fprintf(stderr, "%s: compressing colormap...", programName);
X    fflush(stderr);
X  }
X  ncolors = 0;
X  /* map[] is indexed by old pixel values.  It delivers new, compressed,
X   * pixel values. */
X  for (m=0; m<MAX_CELLS; m++) map[m] = MAX_CELLS+1;
X  /* bludgeon through the whole image and remap each pixel value */
X  for (h=0; h<ximage->height; h++) {
X    for (w=0; w<ximage->width; w++) {
X      /* Get the pixel index and see if it has been used or not.
X       * Then remap the pixel */
X      p = XGetPixel(ximage, w, h);
X      if (map[p] == MAX_CELLS+1) {
X        map[p] = ncolors;
X        ncolors++;
X      }
X      if (p != map[p])
X        XPutPixel(ximage, w, h, map[p]);
X    }
X  }
X  /* now compress the color table */
X  memset(image->used, 0, MAX_CELLS);
X  for (m=0; m<MAX_CELLS; m++) {
X    if (map[m] != MAX_CELLS+1) {
X      p = map[m];
X      nr[p] = image->red[m];
X      ng[p] = image->green[m];
X      nb[p] = image->blue[m];
X      image->used[p] = TRUE;
X    }
X  }
X  memcpy(image->red,   nr, ncolors*sizeof(word));
X  memcpy(image->green, ng, ncolors*sizeof(word));
X  memcpy(image->blue,  nb, ncolors*sizeof(word));
X  image->numcells = ncolors;
X  if (verbose)
X    fprintf(stderr, "  %d colors used\n", ncolors);
X}
X
X
X
X
X
X/*
X * Get the image bounded by the given rectangle.
X * The associated colormap information is also extracted and returned.
X * TRUE is returned if an image was successfully grabbed, and FALSE
X * otherwise.
X */
XgetImage(xrect, image)
X  XRectangle *xrect;
X  imageInfo *image;
X{
X  XImage *ximage;
X  int depth, ncolors, cmapSize, numCmaps;
X  int h, w;
X  long i;
X  XColor colors[MAX_CELLS];
X  Colormap *cmaps, cmap;
X
X  if (xrect->width == 0  || xrect->height == 0)
X    return FALSE;
X
X  depth  = DefaultDepth(hDisplay, hScreen);
X  ximage = XGetImage(hDisplay, hRoot,
X            xrect->x, xrect->y, xrect->width, xrect->height, AllPlanes,
X            depth==1 ? XYPixmap : ZPixmap);
X  image->ximage = ximage;
X
X  /* get the colormap info too */
X
X  cmaps = XListInstalledColormaps(hDisplay, hRoot, &numCmaps);
X  if (numCmaps == 0)
X    cmap = DefaultColormap(hDisplay, hScreen);
X  else {
X    cmap = *cmaps;
X    if (numCmaps > 1)
X      printf(stderr,
X        "%s: more than one colormap found - using first encountered",
X        programName);
X  }
X  XFree(cmaps);
X
X  ncolors = DisplayCells(hDisplay, hScreen);
X  /* this won't cut the mustard for DirectColor */
X  for (i=0; i<ncolors; i++)
X    colors[i].pixel = i;
X      
X  XQueryColors(hDisplay, cmap, colors, ncolors);
X  for (i=0; i<ncolors; i++) {
X    image->red[i]   = colors[i].red;
X    image->green[i] = colors[i].green;
X    image->blue[i]  = colors[i].blue;
X  }
X
X  /* figure out which colormap entries are actually used by the image */
X  ncolors = cmapSize = 0;
X  memset(image->used, 0, MAX_CELLS);
X  for (h=0; h<ximage->height; h++)
X    for (w=0; w<ximage->width; w++) {
X      i = XGetPixel(ximage, w, h);
X      if (!image->used[i]) {
X        image->used[i] = TRUE;
X        if (i+1 > cmapSize)      /* keep track of colormap size */
X          cmapSize = i+1;
X        ncolors++;
X      }
X    }
X  image->numcells = cmapSize;
X  if (verbose)
X    fprintf(stderr, "%s: image has %d colors\n", programName, ncolors);
X
X  return TRUE;
X}
X
X
X
X/*
X * Let the user stretch a rectangle on the screen and return its values.
X * It may be wise to grab the server before calling this routine.  If the
X * screen is allowed to change during XOR drawing video droppings may result.
X */
XgetRectangle(xrect)
X  XRectangle *xrect;
X{
X  XEvent event;
X  unsigned int mask, x, y, rootx, rooty;
X  GC gc;
X  Cursor pointer;
X  int boxDrawn = False;
X  int rx, ry, rw, rh;
X  Window root, child;
X  int discarded;
X  
X  /* get some cursors for rectangle formation */
X  pointer = XCreateFontCursor(hDisplay, XC_crosshair);
X
X  /* grab the pointer */
X  if (GrabSuccess != XGrabPointer(hDisplay, hRoot, False, ButtonPressMask,
X        GrabModeAsync, GrabModeAsync, hRoot, pointer, CurrentTime)) {
X    fprintf(stderr,"%s - could not grab pointer!\n", programName);
X    exit(3);
X  }
X
X  /* create a graphics context to draw with */
X  gc = XCreateGC(hDisplay, hRoot, 0, NULL);
X  if (!gc) {
X    fprintf(stderr,"%s - could not get drawing resources\n", programName);
X    exit(3);
X  }
X  XSetSubwindowMode(hDisplay, gc, IncludeInferiors);
X  XSetForeground(hDisplay, gc, 255);
X  XSetFunction(hDisplay, gc, GXxor);
X
X  /* get a button-press and pull out the root location */
X  XMaskEvent(hDisplay, ButtonPressMask, &event);
X  rootx = rx = event.xbutton.x_root;
X  rooty = ry = event.xbutton.y_root;
X  
X  /* get pointer motion events */
X  XChangeActivePointerGrab(hDisplay, ButtonMotionMask | ButtonReleaseMask,
X        pointer, CurrentTime);
X
X
X  /* MAKE_RECT converts the original root coordinates and the event root
X   * coordinates into a rectangle in xrect */
X#define MAKE_RECT(etype) \
X  x = event.etype.x_root;       \
X  y = event.etype.y_root;       \
X  rw  = x - rootx;              \
X  if (rw  < 0) rw  = -rw;       \
X  rh  = y - rooty;              \
X  if (rh  < 0) rh  = -rh;       \
X  rx = x < rootx ? x : rootx;   \
X  ry = y < rooty ? y : rooty
X
X  /* loop to let the user drag a rectangle */
X  while (TRUE) {
X    XNextEvent(hDisplay, &event);
X    switch(event.type) {
X      case ButtonRelease:
X        if (boxDrawn) {
X          XDrawRectangle(hDisplay, hRoot, gc, rx, ry, rw, rh);
X          boxDrawn = False;
X        }
X        XFlush(hDisplay);
X        /* record the final location */
X        MAKE_RECT(xbutton);
X        /* release resources */
X        XFreeGC(hDisplay, gc);
X        XFreeCursor(hDisplay, pointer);
X        xrect->x      = rx;
X        xrect->y      = ry;
X        xrect->width  = rw;
X        xrect->height = rh;
X        XUngrabPointer(hDisplay, CurrentTime);
X        if (verbose)
X          fprintf(stderr, "%s: rectangle is %d@%d,  %dx%d\n", programName,
X              xrect->x, xrect->y, xrect->width, xrect->height);
X        return True;
X      case MotionNotify:
X        if (boxDrawn) {
X          XDrawRectangle(hDisplay, hRoot, gc, rx, ry, rw, rh);
X          boxDrawn = False;
X        }
X        /* discard incoming motion notifies while we handle this one */
X        discarded = False;
X        while (XCheckTypedEvent(hDisplay, MotionNotify, &event))
X          {}
X        MAKE_RECT(xmotion);
X        XDrawRectangle(hDisplay, hRoot, gc, rx, ry, rw, rh);
X        boxDrawn = True;
X        break;
X    }
X  }
X}
X
X
X
X
X/*
X * convert a pixmap image into a bitmap image
X */
Xpixmap2bitmap(image)
X  imageInfo *image;
X{
X  XImage *ximage = image->ximage;
X  int x, y;
X  word v, black, mid;
X  dw total, blackrgb, midrgb, lowDelta, l;
X  XImage *newImage;
X  byte *newBytes;
X  int usedCount;
X  
X  if (ximage->bits_per_pixel == 1  ||  image->numcells < 1)
X    return;
X
X  /* get the darkest color */
X  blackrgb = 0x2FFFD;  /* 3 * 0xFFFF == white */
X  usedCount = total = 0;
X  for (x=0; x<image->numcells; x++) {
X    if (image->used[x]) {
X      l = (unsigned)image->red[x]
X          +(unsigned)image->green[x]
X          +(unsigned)image->blue[x];
X      if (l <= blackrgb) {
X        black = x;
X        blackrgb = l;
X      }
X      total += l;
X      usedCount++;
X    }
X  }
X  /* now find the mid color and use it as the cut-off for black */
X  midrgb = total / usedCount;
X  lowDelta = 0x2FFFD;
X  for (x=0; x<image->numcells; x++) {
X    if (image->used[x]) {
X      l = (unsigned)image->red[x]
X          +(unsigned)image->green[x]
X          +(unsigned)image->blue[x];
X      l -= midrgb;
X      if (l < lowDelta) {
X        mid = x;
X        lowDelta = l;
X      }
X    }
X  }
X  midrgb = (unsigned)image->red[mid]
X           +(unsigned)image->green[mid]
X           +(unsigned)image->blue[mid];
X    
X  /* create a bitmap image */
X  x = (ximage->width + 7) / 8;
X  newBytes = (byte *)malloc(x * ximage->height);
X  memset(newBytes, 0, x * ximage->height);
X  newImage = XCreateImage(hDisplay, DefaultVisual(hDisplay, hScreen),
X                1, XYBitmap, 0, newBytes, ximage->width, ximage->height,
X                0, x);
X  if (!newImage) {
X    fprintf(stderr, "%s: unable to create bitmap for conversion\n",
X      programName);
X    XCloseDisplay(hDisplay);
X    exit(3);
X  }
X  /* pound the pixels into it */
X  for (y = 0; y < ximage->height; y++) {
X    for (x = 0; x < ximage->width; x++) {
X      v = XGetPixel(ximage, x, y);
X      l = (dw)image->red[v]+(dw)image->green[v]+(dw)image->blue[v];
X      XPutPixel(newImage, x, y, l<midrgb? 1 : 0);
X    }
X  }
X  free(ximage->data);
X  memcpy(ximage, newImage, sizeof(XImage));
X  free(newImage);
X
X  image->numcells = 0;
X}
X
X
X
X
X
X
X
X#define GRAYS    17 /* ((4 * 4) + 1) patterns for a good dither */
X#define GRAYSTEP ((dw)(65536 * 3) / GRAYS)
X
Xstatic byte DitherBits[GRAYS][4] = {
X  0xf, 0xf, 0xf, 0xf,
X  0xe, 0xf, 0xf, 0xf,
X  0xe, 0xf, 0xb, 0xf,
X  0xa, 0xf, 0xb, 0xf,
X  0xa, 0xf, 0xa, 0xf,
X  0xa, 0xd, 0xa, 0xf,
X  0xa, 0xd, 0xa, 0x7,
X  0xa, 0x5, 0xa, 0x7,
X  0xa, 0x5, 0xa, 0x5,
X  0x8, 0x5, 0xa, 0x5,
X  0x8, 0x5, 0x2, 0x5,
X  0x0, 0x5, 0x2, 0x5,
X  0x0, 0x5, 0x0, 0x5,
X  0x0, 0x4, 0x0, 0x5,
X  0x0, 0x4, 0x0, 0x1,
X  0x0, 0x0, 0x0, 0x1,
X  0x0, 0x0, 0x0, 0x0
X};
X
Xstatic byte DitherRevBits[GRAYS][4] = {  /* DitherBits with LSBFirst */
X  0xf, 0xf, 0xf, 0xf,
X  0x7, 0xf, 0xf, 0xf,
X  0x7, 0xf, 0xd, 0xf,
X  0x5, 0xf, 0xd, 0xf,
X  0x5, 0xf, 0x5, 0xf,
X  0x5, 0xb, 0x5, 0xf,
X  0x5, 0xb, 0x5, 0xe,
X  0x5, 0xa, 0x5, 0xe,
X  0x5, 0xa, 0x5, 0xa,
X  0x1, 0xa, 0x5, 0xa,
X  0x1, 0xa, 0x4, 0xa,
X  0x0, 0xa, 0x4, 0xa,
X  0x0, 0xa, 0x0, 0xa,
X  0x0, 0x2, 0x0, 0xa,
X  0x0, 0x2, 0x0, 0x8,
X  0x0, 0x0, 0x0, 0x8,
X  0x0, 0x0, 0x0, 0x0
X};
X
X/* halftone or dither a color image, changing it into a monochrome
X * image
X */
Xpixmap2halftone(image, dither)
X  imageInfo *image;
X  int dither;           /* if TRUE, dither instead of halftone */
X{
X  XImage *ximage = image->ximage;
X  XImage *newImage;
X  byte   *newBytes, *dp, *ditherBits;
X  word   dindex;  /* index into dither array */
X  dw     color;   /* pixel color */
X  word  *index;   /* index into dither array for a given pixel */
X  word   x, y;    /* random counters */
X  word   x4, y4;
X  word   w, h;
X  int    reversed;
X  register byte *dp2;
X  register byte  bits;
X
X  if (verbose) {
X    fprintf(stderr, "%s: %sing image...", programName,
X        dither? "dither" : "halfton");
X    fflush(stderr);
X  }
X
X  /* create a bitmap image */
X  w = (ximage->width + 7) / 8;
X  if (!dither) w *= 4;
X  h = dither? ximage->height : ximage->height * 4;
X  newBytes = (byte *)malloc(w * h);
X  memset(newBytes, 0, w * h);
X  newImage = XCreateImage(hDisplay, DefaultVisual(hDisplay, hScreen),
X                1, XYBitmap, 0, newBytes,
X                ximage->width * (dither? 1 : 4),
X                ximage->height * (dither? 1 : 4),
X                0, w);
X  if (!newImage) {
X    fprintf(stderr, "%s: unable to create bitmap for conversion\n",
X      programName);
X    XCloseDisplay(hDisplay);
X    exit(3);
X  }
X
X
X  /* if the number of possible pixels isn't very large, build an array
X   * which we index by the pixel value to find the dither array index
X   * by color brightness.  we do this in advance so we don't have to do
X   * it for each pixel.  things will break if a pixel value is greater
X   * than (1 << depth), which is bogus anyway.  this calculation is done
X   * on a per-pixel basis if the colormap is too big.
X   */
X
X  if (ximage->depth <= 16) {
X    index= (word *)malloc(sizeof(word) * MAX_CELLS);
X    if (index)
X      for (x= 0; x < image->numcells; x++) {
X        index[x] =
X          ((dw)(image->red[x])
X           + (dw)(image->green[x])
X           + (dw)(image->blue[x])  ) / GRAYSTEP;
X        if (index[x] >= GRAYS)
X          index[x] = GRAYS - 1;
X      }
X  }
X  else
X    index= NULL;
X
X  /* dither each pixel
X   */
X  reversed = newImage->bitmap_bit_order == LSBFirst;
X  for (y= 0; y < ximage->height; y++) {
X    for (x= 0; x < ximage->width; x++) {
X      color = XGetPixel(ximage, x, y);
X      if (index)
X	dindex= index[color];
X      else {
X	dindex= ((dw)image->red[color]
X		 +(dw)image->green[color]
X		 +(dw)image->blue[color] ) / GRAYSTEP;
X        if (dindex >= GRAYS)  /* catch rounding errors */
X	  dindex= GRAYS - 1;
X      }
X      if (dither) {
X        if (DitherBits[dindex][y & 3] & (1 << (x & 3)))
X           XPutPixel(newImage, x, y, 1);
X      }
X      else { /* halftone */
X        /* loop for the four Y bits in the dither pattern, putting all
X         * four X bits in at once.  if you think this would be hard to
X         * change to be an NxN dithering array, you're right, since we're
X         * banking on the fact that we need only shift the mask based on
X         * whether x is odd or not.  an 8x8 array wouldn't even need that,
X         * but blowing an image up by 64x is probably not a feature.
X         */
X        ditherBits = reversed? &(DitherRevBits[dindex][0])
X                             : &(DitherBits[dindex][0]);
X        x4 = x * 4;
X        y4 = y * 4;
X        for (h= 0; h < 4; h++) {
X          bits = ditherBits[h];
X          for (w=0; w < 4; w++)
X            XPutPixel(newImage, x4+w, y4+h, (bits >> 0) & 1);
X        }
X      }
X    }
X    if (dither)
X      dp += newImage->bytes_per_line;
X    else
X      dp += 4 * newImage->bytes_per_line;
X  }
X  if (verbose)
X    fputc('\n', stderr);
X
X  free(ximage->data);
X  memcpy(ximage, newImage, sizeof(XImage));
X  free(newImage);
X  if (index)
X    free(index);
X
X  image->numcells = 0;
X}
X
X
X
X
X
X/* swap the bits in a byte */
Xswapbits(b)
X  byte b;
X{
X  byte b2;
X  
X  b2 = 0;
X  b2 |= (b & 0x01) << 7;
X  b2 |= (b & 0x02) << 5;
X  b2 |= (b & 0x04) << 3;
X  b2 |= (b & 0x08) << 1;
X  b2 |= (b & 0x10) >> 1;
X  b2 |= (b & 0x20) >> 3;
X  b2 |= (b & 0x40) >> 5;
X  b2 |= (b & 0x80) >> 7;
X  return b2;
X}
X
X
X
X  
X/* swap the bytes in a long int */
Xswapbytes(pDblw)
X  dw *pDblw;
X  {
X  union {
X    dw  dbl;
X    byte bytes[4];
X    } cnv;
X  byte aByte;
X  
X  cnv.dbl = *pDblw;
X  aByte = cnv.bytes[0];
X  cnv.bytes[0] = cnv.bytes[3];
X  cnv.bytes[3] = aByte;
X  aByte = cnv.bytes[1];
X  cnv.bytes[1] = cnv.bytes[2];
X  cnv.bytes[2] = aByte;
X  *pDblw = cnv.dbl;
X  }
X
X
X
X/* swap some long ints */
Xswapdws (bp, n)
X  register char *bp;
X  register unsigned n;
X{
X  register char c;
X  register char *ep = bp + n;
X  register char *sp;
X
X  while (bp < ep) {
X    sp = bp + 3;
X    c = *sp;
X    *sp = *bp;
X    *bp++ = c;
X    sp = bp + 1;
X    c = *sp;
X    *sp = *bp;
X    *bp++ = c;
X    bp += 2;
X  }
X}
X
X
X
X/* swap some short ints */
Xswapwords (bp, n)
X  register char *bp;
X  register unsigned n;
X{
X  register char c;
X  register char *ep = bp + n;
X
X  while (bp < ep) {
X    c = *bp;
X    *bp = *(bp + 1);
X    bp++;
X    *bp++ = c;
X  }
X}
X
X
X
X
X
X/*
X * Write an image in Postscript format
X */
XwritePostscript(image, outfile)
X  imageInfo *image;
X{
X  XImage *ximage = image->ximage;
X  XImage *psimage;
X  double xdpi, ydpi, xscale, yscale;
X  byte b, *ptr, lmax;
X  int lm3;
X  int x, y, i;
X  int depth, bpl, spb;
X  int reverse;
X  int rle, firstrle;
X  byte rlecount, rlebyte;
X  dw  rletotal;
X  long p;
X  
X  if (verbose)
X    fprintf(stderr, "%s: formatting Postscript output\n", programName);
X
X  /* use depth as the number of bits in output samples */
X  depth = ximage->depth;
X  /* postscript only supports 1, 2, 4, or 8 */
X  if (depth > 8) depth = 8;     /* max postscript bits/sample */
X  if (depth < 8 && depth > 4) depth = 8;
X  if (depth == 3) depth = 4;
X
X  bpl = ((ximage->width * depth) + 7) / 8;
X
X  if (depth == 1)
X    psimage = ximage;
X  else {
X    /* colors have to be changed to luminescence */
X    ptr = (byte *)malloc(ximage->height * bpl);
X    psimage = XCreateImage(hDisplay, DefaultVisual(hDisplay, hScreen),
X                  depth, depth>1? ZPixmap : XYPixmap,
X                  0, ptr,
X                  ximage->width, ximage->height,
X                  0, bpl);
X    if (!psimage) {
X      fprintf(stderr, "%s: could not create image for Postscript conversion\n",
X        programName);
X      exit(3);
X    }
X  }
X  
X  spb = 8 / psimage->bits_per_pixel;    /* samples per byte */
X
X  if (depth > 1) {
X    /* translate colors into luminescence */
X    lmax = (1 << psimage->bits_per_pixel) - 1;
X    lm3   = 3 * lmax;
X    for (y = 0; y < ximage->height; y++) {
X      for (x = 0; x < ximage->width; x++) {
X        p = XGetPixel(ximage, x, y);
X        i = (dw)image->red[p]+(dw)image->green[p]+(dw)image->blue[p];
X        i = (i * lmax) / lm3;
X        XPutPixel(psimage, x, y, i);
X      }
X    }
X  }
X  
X  /* see if the image will benefit from run-length encoding */
X  rlecount = 0xff;
X  rletotal = 0;
X  for (y=0; y<psimage->height; y++) {
X    for (x=0, ptr=(byte *)(psimage->data+(y * psimage->bytes_per_line));
X         x<psimage->width;
X         x+=spb, ptr++) {
X      b = *ptr;
X      if (b != rlebyte || rlecount == 0xff) {
X        rletotal += 2;
X        rlecount = 0;
X        rlebyte  = b;
X      }
X      else
X        rlecount++;
X    }
X  }
X  rle = rletotal < psimage->height * bpl;
X
X  fprintf(outfile, "%%!\n");
X  fprintf(outfile, "%%\n");
X  fprintf(outfile, "%% Xgrabsc Postscript dump of image '%s'\n", imageName);
X  fprintf(outfile, "%%\n");
X  fprintf(outfile, "%%\n");
X
X  if (rle) {
X    fprintf(outfile, "%% run-length encoded.  Savings = %d bytes\n",
X            (psimage->height * bpl - rletotal) * 2);
X    fprintf(outfile, "%%\n");
X    fprintf(outfile, "%%\n");
X  }
X
X  /* standard inch procedure */
X  fputs("/inch {72 mul} def\n", outfile);
X
X  /* define a string to hold image lines */
X  fprintf(outfile, "/picstr %d string def\n", bpl);
X  
X  /* define a string to hold run-length-encoded pairs */
X  if (rle)
X    fputs("/rlebuffer 2 string def\n", outfile);
X
X  /* define the image plotting procedure */
X  fputs("/plotimage\n", outfile);
X
X    /* parameters for the standard image procedure */
X    fprintf(outfile, "  {%d %d %d [%d 0 0 -%d 0 %d]\n",
X          psimage->width, psimage->height, psimage->bits_per_pixel,
X          psimage->width, psimage->height, psimage->height);
X  
X    /* line reading function  */
X    if (rle) {
X      fputs("  { currentfile rlebuffer readhexstring pop pop\n", outfile);
X      fputs("    /nsamples rlebuffer 0 get 1 add store\n", outfile);
X      fputs("    /lum      rlebuffer 1 get store\n", outfile);
X      fputs("    /samples  nsamples string store\n", outfile);
X      fputs("    0 1 nsamples -1 add { samples exch lum put } for\n",
X            outfile);
X      fputs("    samples\n", outfile);
X      fputs("  }\n", outfile);
X    }
X    else
X      fputs("  {currentfile picstr readhexstring pop}\n", outfile);
X  
X  fputs("  image\n} def\n", outfile);
X  
X
X  /* save context and move to a nice origin */
X  fputs("gsave\n", outfile);
X  
X
X  /* scale the image */
X  xdpi = (((double)DisplayWidth(hDisplay,hScreen)) * 25.4) /
X          ((double)DisplayWidthMM(hDisplay,hScreen));
X  ydpi = (((double)DisplayHeight(hDisplay,hScreen)) * 25.4) /
X          ((double)DisplayHeightMM(hDisplay,hScreen));
X  xscale = ((double)psimage->width) / xdpi;
X  yscale = ((double)psimage->height) / ydpi;
X  if (xscale > 7.5) {
X    yscale *= 7.5 / xscale;
X    xscale = 7.5;
X  }
X  else if (yscale > 10.0) {
X    xscale *= 10.0 / yscale;
X    yscale = 10.0;
X  }
X  fprintf(outfile, "%1.2g inch %1.2g inch translate\n",
X                (8.5 - xscale) / 2.0, (11.0 - yscale) / 2.0);
X  fprintf(outfile, "%1.2g inch %1.2g inch scale\n", xscale, yscale);
X  
X  fputs("plotimage\n", outfile);
X  
X
X  reverse = depth == 1? BlackPixel(hDisplay,hScreen)==1 : FALSE;
X  rletotal = 0;
X  rlecount = 0xff;
X  for (y=0; y<psimage->height; y++) {
X    for (x=0, ptr=(byte *)(psimage->data+(y * psimage->bytes_per_line));
X         x<psimage->width;
X         x+=spb, ptr++) {
X      b = *ptr;
X      if (reverse) b = ~b;
X      if (depth == 1  &&  psimage->bitmap_bit_order == LSBFirst)
X        b = swapbits(b);
X      if (rle) {
X        if (b != rlebyte  ||  rlecount == 0xff) {
X          if (rletotal)  /* == 0 first time through main loop */
X            fprintf(outfile, "%02x%02x", rlecount, rlebyte);
X          rletotal += 2;
X          if (rletotal % 200 == 0)
X            fputs("\n", outfile);
X          rlecount = 0;
X          rlebyte  = b;
X        }
X        else
X          rlecount++;
X      }
X      else
X        fprintf(outfile, "%02x", b);
X    }
X    if (!rle)
X      fputs("\n", outfile);
X  }
X  if (rle  &&  rletotal)
X    fprintf(outfile, "%02x%02x\n", rlecount, rlebyte);
X
X  fputs("\n\n\ngrestore\nshowpage\n", outfile);
X
X
X  if (psimage != ximage) {
X    free(psimage->data);
X    free(psimage);
X  }
X}
X
X
X
X
X
X
X
X
X
X/*
X * Write an image in 'puzzle' format, suitable for loading with
X * "puzzle -picture".
X */
XwritePuzzle(image, outfile)
X  imageInfo *image;
X  FILE *outfile;
X{
X  XImage *ximage = image->ximage;
X  int nc, width, height, w, h, cidx;
X  dw swaptest = 1;
X
X  if (verbose)
X    fprintf(stderr, "%s: formatting Puzzle output\n", programName);
X
X  if (ximage->depth > 8) {
X    fprintf(stderr, "%s: Puzzle converter can't handle depth > 8 yet\n",
X            programName);
X    return;
X  }
X
X  nc     = image->numcells;
X  width  = ximage->width;
X  height = ximage->height;
X  if (*(char *)&swaptest) {
X    swapbytes(&width);
X    swapbytes(&height);
X  }
X  fwrite(&width, 4, 1, outfile);
X  fwrite(&height, 4, 1, outfile);
X  fputc(nc, outfile);
X  for (cidx=0; cidx<nc; cidx++) {
X    fputc(image->red[cidx]>>8,   outfile);
X    fputc(image->green[cidx]>>8, outfile);
X    fputc(image->blue[cidx]>>8,  outfile);
X  }
X  for (h=0; h<ximage->height; h++)
X    if (ximage->bits_per_pixel == 8)
X      fwrite(ximage->data+(h*ximage->bytes_per_line),ximage->width,1,outfile);
X    else
X      /* this won't work if depth > 8 */
X      for (w=0; w<ximage->width; w++)
X        fputc(XGetPixel(ximage, w, h), outfile);
X}
X
X
X
X
X
X
X
XwriteXWD(image, outfile)
X  imageInfo *image;
X  FILE *outfile;
X{
X  XImage   *ximage = image->ximage;
X  XWDFileHeader header;
X  Visual   *visual = DefaultVisual(hDisplay, hScreen);
X  XColor    color;
X  dw        visMask = (visual->red_mask
X                      | visual->green_mask
X                      | visual->blue_mask);
X  dw        swaptest = 1;
X  int       i;
X
X  if (verbose)
X    fprintf(stderr, "%s: formatting xwd output\n", programName);
X
X  header.header_size    = (CARD32)(sizeof(header)+strlen(imageName)+1);
X  header.file_version   = (CARD32) XWD_FILE_VERSION;
X  header.pixmap_format  = (CARD32)(ximage->depth>1? ZPixmap : XYPixmap);
X  header.pixmap_depth   = (CARD32) ximage->depth;
X  header.pixmap_width   = (CARD32) ximage->width;
X  header.pixmap_height  = (CARD32) ximage->height;
X  header.xoffset        = (CARD32) ximage->xoffset;
X  header.byte_order     = (CARD32) ximage->byte_order;
X  header.bitmap_unit    = (CARD32) ximage->bitmap_unit;
X  header.bitmap_bit_order = (CARD32) ximage->bitmap_bit_order;
X  header.bitmap_pad     = (CARD32) ximage->bitmap_pad;
X  header.bits_per_pixel = (CARD32) ximage->bits_per_pixel;
X  header.bytes_per_line = (CARD32) ximage->bytes_per_line;
X  header.visual_class   = (CARD32)visual->class;
X  header.red_mask       = (CARD32)visual->red_mask;
X  header.green_mask     = (CARD32)visual->green_mask;
X  header.blue_mask      = (CARD32)visual->blue_mask;
X  header.bits_per_rgb   = (CARD32)visual->bits_per_rgb;
X  header.colormap_entries = (CARD32)visual->map_entries;
X  header.ncolors        = image->numcells;
X  header.window_width   = (CARD32)ximage->width;
X  header.window_height  = (CARD32)ximage->height;
X  header.window_x       = 0;
X  header.window_y       = 0;
X  header.window_bdrwidth = 0;
X
X  if (*(char *) &swaptest)
X    swapdws(&header, sizeof(header));
X
X  fwrite(&header, sizeof(header), 1, outfile);
X  fwrite(imageName, 1, strlen(imageName)+1, outfile);
X
X  for (i=0; i<image->numcells; i++) {
X    color.pixel = i;
X    color.red   = image->red[i];
X    color.green = image->green[i];
X    color.blue  = image->blue[i];
X    color.flags = visMask;
X    color.pad   = 0;
X    if (*(char *) &swaptest)
X      swapwords(&color, sizeof(XColor));
X    fwrite(&color, sizeof(XColor), 1, outfile);
X  }
X
X  fwrite(ximage->data, ximage->height * ximage->bytes_per_line, 1, outfile);
X}
X
X
X
X
X
X/*
X * Write a monochrome image out in Bitmap format.  XWriteBitmapToFile
X * requires a Pixmap as input & we'd have to invent one before we could
X * use it.
X */
X
XwriteXYPixmap(image, outfile)
X  imageInfo *image;
X  FILE *outfile;
X{
X  XImage *ximage = image->ximage;
X  int w, h;
X  byte b, *line;
X  int lcount;
X  int reverse = BlackPixel(hDisplay, hScreen) == 0;
X  int swap    = ximage->bitmap_bit_order != LSBFirst;
X
X  if (verbose)
X    fprintf(stderr, "%s: formatting Bitmap output\n", programName);
X
X  if (ximage->depth != 1) {
X    fprintf(stderr, "%s: can't write polychrome images in XY bitmap format\n",
X      programName);
X    return;
X  }
X
X  fprintf(outfile, "#define %s_width %d\n",  imageName, ximage->width);
X  fprintf(outfile, "#define %s_height %d\n", imageName, ximage->height);
X  fprintf(outfile, "#define %s_x_hot 0\n",   imageName);
X  fprintf(outfile, "#define %s_y_hot 0\n",   imageName);
X  fprintf(outfile, "static char %s_bits[] = {\n", imageName);
X  lcount = 0;
X  fputs("  ", outfile);
X  for (h=0; h<ximage->height; h++) {
X    line = (byte *)(ximage->data + (h * ximage->bytes_per_line));
X    for (w=0; w<ximage->width; w+=8) {
X      b = line[w/8];
X      if (reverse) b = ~b;
X      if (swap)    b = swapbits(b);
X      fprintf(outfile, " 0x%02x", b);
X      if (h<ximage->height || w+8<ximage->width)
X        fputc(',', outfile);
X      lcount++;
X      if (lcount >= 12) {
X        fputs("\n  ", outfile);
X        lcount = 0;
X      }
X    }
X  }
X  fputs("  };\n", outfile);
X}
X
X
X
X
X
X
X
X
X/*
X * Write a color image out in Pixmap format, suitable for loading with
X * "xpd" or "xloadimage".  Note that "xpd" usually fails miserably if
X * the image is wider than 255 characters in the output file.
X */
XwriteZPixmap(image, outfile)
X  imageInfo *image;
X  FILE *outfile;
X{
X  XImage *ximage = image->ximage;
X  int nc, width, height, w, h, cidx, cpp;
X  char mne[MAX_CELLS][3];
X
X  if (verbose)
X    fprintf(stderr, "%s: formatting Pixmap output\n", programName);
X
X  nc  = image->numcells;
X  cpp = image->numcells <= 26? 1 : 2;
X  fprintf(outfile, "#define %s_format 1\n",   imageName);
X  fprintf(outfile, "#define %s_width %d\n",   imageName, ximage->width);
X  fprintf(outfile, "#define %s_height %d\n",  imageName, ximage->height);
X  fprintf(outfile, "#define %s_ncolors %d\n", imageName, image->numcells);
X  fprintf(outfile, "#define %s_chars_per_pixel %d\n",     imageName, cpp);
X  fprintf(outfile, "static char * %s_colors[] = {\n", imageName);
X  for (cidx=0; cidx<image->numcells; cidx++) {
X    if (cpp > 1) {
X      mne[cidx][0] = (char)(cidx / 10) + 'a';
X      mne[cidx][1] = (char)(cidx % 10) + '0';
X      mne[cidx][2] = '\0';
X    }
X    else {
X      mne[cidx][0] = (char)cidx + (cidx? 'A' : ' ');
X      mne[cidx][1] = '\0';
X    }
X    fprintf(outfile, "\"%s\", \"#%4.4x%4.4x%4.4x\"\n", mne[cidx],
X                image->red[cidx], image->green[cidx], image->blue[cidx]);
X  }
X  fputs("} ;\n", outfile);
X  fprintf(outfile, "static char * %s_pixels[] = {\n", imageName);
X  for (h=0; h<ximage->height; h++) {
X    fputs("\"", outfile);
X    for (w=0; w<ximage->width; w++)
X      fputs(mne[XGetPixel(ximage, w, h)], outfile);
X    fputs("\",\n", outfile);
X  }
X  fputs("} ;\n", outfile);
X}
X
X
X
X
X
X
X
X
Xmain(argc, argv)
X  int argc;
X  char *argv[];
X{
X  FILE *outfile;
X  char *outfileName;
X  XRectangle xrect;
X  imageInfo image;
X  int doAnd, doOr, depth;
X  int puzzle, xWD, brighten, postscript;
X  int forceBitmap, grabServer;
X  int dither, halftone;
X  int sleepSeconds;
X  int andBits, orBits;
X  char c;
X  char *ptr;
X  char *display;
X  int i, nr, nc;
X  extern char *optarg;
X  int brightenFactor;
X  
X  outfile     = stdout;
X  outfileName = NULL;
X  programName = argv[0];
X  puzzle      = FALSE;
X  xWD         = FALSE;
X  brighten    = FALSE;
X  forceBitmap = FALSE;
X  halftone    = FALSE;
X  dither      = FALSE;
X  grabServer  = TRUE;
X  postscript  = FALSE;
X  doAnd       = FALSE;
X  doOr        = FALSE;
X  sleepSeconds= 0;
X  display     = NULL;
X  verbose     = FALSE;
X  
X  while ((c = getopt(argc, argv, "d:no:s:v b:A:BDHO: PWZ")) != EOF)
X    switch (c) {
X      case 'd':
X        display = optarg;
X        break;
X      case 'n':
X        grabServer = FALSE;
X        break;
X      case 'o':
X        outfileName = optarg;
X        break;
X      case 's':
X        sleepSeconds = atoi(optarg);
X        if (sleepSeconds < 0) sleepSeconds = 0;
X        break;
X      case 'v':
X        verbose = TRUE;
X        break;
X
X
X
X
X      case 'A':
X        andBits = atoi(optarg);
X        doAnd = TRUE;
X        break;
X      case 'b':
X        brightenFactor = atoi(optarg);
X        if (brightenFactor <= 0) {
X          fprintf(stderr, "%s: brightening factor must be a positive number\n",
X            programName);
X          exit(3);
X        }
X        brighten = TRUE;
X        break;
X      case 'B':
X        forceBitmap = TRUE;
X        break;
X      case 'D':
X        dither = TRUE;
X        if (halftone) {
X          fprintf(stderr,
X            "%s: both dither and halftone requested.  Ignoring halftone.",
X            programName);
X          halftone = FALSE;
X        }
X        break;
X      case 'H':
X        halftone = TRUE;
X        if (dither) {
X          fprintf(stderr,
X            "%s: both dither and halftone requested.  Ignoring halftone.",
X            programName);
X          halftone = FALSE;
X        }
X        break;
X      case 'O':
X        orBits = atoi(optarg);
X        doOr = TRUE;
X        break;
X
X
X
X
X      case 'P':
X        postscript = TRUE;
X        if (xWD | puzzle) {
X          fprintf(stderr,
X            "%s: only one output format allowed.  Using postscript.\n",
X            programName);
X          xWD = puzzle = FALSE;
X        }
X        break;
X      case 'W':
X        xWD = TRUE;
X        if (puzzle | postscript) {
X          fprintf(stderr,
X            "%s: can't do both puzzle and XWD output.  Using XWD.\n",
X            programName);
X          puzzle = postscript = FALSE;
X        }
X        break;
X      case 'Z':
X        puzzle = TRUE;
X        if (xWD | postscript) {
X          fprintf(stderr,
X            "%s: only one output format allowed.  Using puzzle.\n",
X            programName);
X          xWD = postscript = FALSE;
X        }
X        break;
X    }
X
X  if (!display) display = (char *)getenv("DISPLAY");
X  hDisplay = XOpenDisplay(display);
X  if (!hDisplay) {
X    fprintf(stderr, "%s: could not open X display\n", programName);
X    exit(3);
X  }
X  hScreen  = DefaultScreen(hDisplay);
X  hRoot    = DefaultRootWindow(hDisplay);
X
X  depth  = DefaultDepth(hDisplay, hScreen);
X  if (DisplayCells(hDisplay, hScreen) > MAX_CELLS) {
X    fprintf(stderr, "%s: color table is too big for this program\n",
X      programName);
X    XCloseDisplay(hDisplay);
X    exit(3);
X  }
X  
X  /* sleep if asked to do so */
X  if (sleepSeconds)
X    sleep(sleepSeconds);
X  
X  /* grab the screen if asked to do so */
X  if (grabServer)
X    XGrabServer(hDisplay);
X
X  /* let the user drag out a rectangle on the screen */
X  if (!getRectangle(&xrect)) {
X    XCloseDisplay(hDisplay);
X    exit(3);
X  }
X  
X  /* get the image bounded by the rectangle */
X  if (!getImage(&xrect, &image)) {
X    XCloseDisplay(hDisplay);
X    exit(3);
X  }
X
X  if (grabServer) {
X    XUngrabServer(hDisplay);
X    XFlush(hDisplay);
X  }
X
X  /* do color image processing/conversions */
X  if (depth >= 2) {
X    if (brighten)
X      brightenColors(&image, brightenFactor);
X    if (doAnd)
X      alterPlanes(&image, TRUE, andBits);
X    if (doOr)
X      alterPlanes(&image, FALSE, orBits);
X
X    if (forceBitmap) {
X      pixmap2bitmap(&image);
X      depth = 1;
X    }
X    else if (halftone | dither)
X      pixmap2halftone(&image, dither);
X    else
X      compressColormap(&image);
X  }
X
X
X  /* open the output stream */
X  if (outfileName) {
X    outfile = fopen(outfileName, "w");
X    if (!outfile) {
X      fprintf(stderr, "%s: ", programName);
X      perror(outfileName);
X      exit(3);
X    }
X    ptr = rindex(outfileName, '.');
X    if (ptr) *ptr = '\0';
X    imageName = rindex(outfileName, '/');
X    if (imageName) imageName++;
X    else imageName = outfileName;
X  }
X  else
X    imageName = "unnamed";   /* default for image names */
X
X
X  /* garbage in --> garbage out */
X  if (postscript)
X    writePostscript(&image, outfile);
X  else if (xWD)
X    writeXWD(&image, outfile);
X  else if (puzzle)
X    writePuzzle(&image, outfile);
X  else if (image.ximage->depth <= 1)
X    writeXYPixmap(&image, outfile);
X  else
X    writeZPixmap(&image, outfile);
X
X  XDestroyImage(image.ximage);
X  XCloseDisplay(hDisplay);
X  if (outfileName)
X    fclose(outfile);
X
X  exit(0);
X}
X
END_OF_FILE
if test 38224 -ne `wc -c <'xgrabsc.c'`; then
    echo shar: \"'xgrabsc.c'\" unpacked with wrong size!
fi
# end of 'xgrabsc.c'
fi
if test -f 'xgrabsc.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xgrabsc.man'\"
else
echo shar: Extracting \"'xgrabsc.man'\" \(7589 characters\)
sed "s/^X//" >'xgrabsc.man' <<'END_OF_FILE'
X.\"========================================================================
X.\"
X.\" Name - xgrabsc.man
X.\"
X.\" Version:	1.2
X.\"
X.\" ccsid:	@(#)xgrabsc.man	1.2 - 8/17/90 09:07:01
X.\" from: 	ccs/s.xgrabsc.man
X.\" date: 	8/20/90 09:56:03
X.\"
X.\" Copyright (C) 1990, Bruce Schuchardt
X.\" See the end of this document for full copyright information.
X.\"
X.\" Description:  Man page for xgrabsc
X.\"
X.\"========================================================================
X.\"
X.TH XGRABSC 1X
X.\"
X.SH NAME
Xxgrabsc \- grab rectangular screen images and store in files
X.\"
X.SH SYNTAX
X\fIxgrabsc\fR \ [options]
X.\"
X.SH DESCRIPTION
X\fIxgrabsc\fR lets you grab arbitrary rectangular images from an
XX server and writes them to standard output in a variety of formats.
X.PP
XCommand line options also allow reduction of colormaps, halftoning
Xand dithering of color images, and direct mapping of color images
Xto monochrome.
X.PP
XThe default output formats are X Pixmap for color images and X Bitmap for
Xmonochrome bitmaps.
X.SH OPTIONS
X.TP 8
X-d \fIdisplayName\fP
XUse an alternate display.
X.TP
X-n
XInhibit server grabs.  Normally xgrabsc will "grab" the server so
Xthat the screen is frozen while a rectangle is selected and the image
Xis extracted.  If the screen is not frozen, rubber-banding may cause
Xvideo droppings on portions of the screen that are changing.
X.TP
X-o \fIoutput-file\fP
XWrite output to \fIoutput-file\fP instead of standard output.
X.TP
X-s \fIseconds\fP
XSleep for \fIseconds\fP seconds before commencing operation.  This
Xshould be used if you need some time to get the target image ready.
X.TP
X-v
XDisplay processing information on standard error output (stderr).
X.sp 3
X.TP
X-b \fIpercent\fR
Xbrighten or darken the image by \fIpercent\fR.  Percentages are given
Xas integers.  As in \fIxloadimage\fR, 100 is the base and a larger
Xnumber will brighten the image while a smaller number will darken the
Ximage.
X.TP
X-A \fIandBits\fR
XClear all colormap bits up to the given plane.  This has the effect of
Xdarkening the image somewhat and shrinking the apparent depth of the image
X(and, consequently, the size of the color table).  \fIAndBits\fR should
Xbe in the range [1-8] inclusive.
X.TP
X-O \fIorBits\fR
XSet all colormap bits up to the given plane.  This brightens the image
Xsomewhat and also shrinks the apparent depth of the image.  When
Xboth \-A and \-O are specified, ANDing will occur before ORing.
X.TP
X-B
XConvert the source color image to a monochrome bitmap.  All colors
Xfalling below the average color intensity are mapped to black.  Others
Xare mapped to white.
X.TP
X-D
XConvert the source color image to a dithered monochrome bitmap.
XThis is like halftoning, but resolution is sacrificed to keep the
Xresulting image the same size as the original.
X.TP
X-H
XConvert the source color image to a halftoned monchrome bitmap.
XResolution is maintained by increasing the size of the image by
Xa factor of four on both axes.
X.sp 3
X.TP
X-P
XWrite output in \fIPostscript\fP format.  Output will be run-length-encoded
Xif encoding will result in any savings.  The number of bits per
XPostscript sample is determined by the depth of the image.
X.TP
X-W
XWrite output in \fIxwd\fP format.
X.TP
X-Z
XWrite output in a format suitable for loading into the \fIpuzzle\fP
Xprogram (see example below).
X.sp 2
X.SH PROCESSING ORDER
XIt is helpful to know the order of processing when multiple processing
Xoptions are given on the command line.
X.PP
XProcessing is done in five phases:  1) set up, 2) obtain image,
X3) process colors, 4) poly->monochrome conversions, and 5) output conversion.
X.PP
XThe set-up phase includes processing command-line options, sleeping,
Xconnecting to X-Windows, freezing the screen and grabbing the mouse.
X.PP
XAfter the mouse is grabbed, rubber-banding occurs until a mouse button
Xis released.  The image is then pulled from the screen and the mouse
Xand screen are released.
X.PP
XIf the image is not monochrome, the color manipulation functions are
Xthen applied in this order: brighten, AND, and OR.
X.PP
XOnly one polychrome to monochrome conversion is allowed.  If none of
Xthese is chosen, the color table of a polychrome image is compressed
Xin preparation for output conversion.
X.PP
XThe output stream is then opened and the image is written in the selected
Xoutput format.
X.sp 2
X.SH EXAMPLES
XThe simplest form of use, giving X11 Bitmap or Pixmap output,  is
X.sp
X.ti +5
Xxgrabsc >outfile.xpm
X.sp
X.PP
XTo write output in \fIPostscript\fP format and send to the printer,
Xuse
X.sp
X.ti +5
Xxgrabsc -P | lpr
X.sp
XIt is sometimes helpful to brighten an image somewhat before it is
Xformatted for Postscript output.  E.g., to brighten by 30%
X.sp
X.ti +5
Xxgrabsc -Pb 130 | lpr
X.sp
X.PP
XTo write output in \fIpuzzle\fP format and read into the puzzle
Xprogram, use the commands
X.sp
X.ti +5
Xxgrabsc -Z >outfile.pzl
X.br
X.ti +5
Xpuzzle -picture outfile.pzl
X.sp
X.PP
XTo have xgrabsc sleep for three seconds before rubber-banding, display
Xprocessing information, and have the result displayed with xwud,
X.sp
X.ti +5
Xxgrabsc -Wvs3 | xwud
X.sp
X.PP
XTo grab an image from another server and then reduce the colormap
Xto three bits by ANDing, use
X.sp
X.ti +5
Xxgrabsc -dother:0.0 -A5 >outfile.xpm
X.sp
XYou will, of course, have to go to the other machine to select the
Ximage with that machine's mouse.
X.sp 2
X.SH LIMITATIONS
XColormaps larger than 256 entries are not currently supported.
X.PP
XThe default screen visual is used as the visual for the image.
XVisuals are associated with particular windows, and xgrabsc pretends
Xignorance about any windows but the root.
X.PP
XThis software has been tested with StaticGray and 8-plane PseudoColor
Xon DECStations (using both UWS 2.2 and X11 Release 4).  It has also
Xbeen tested with 8-plane PseudoColor
Xon Sun SparcStations using X11 Release 4.
X.PP
XX11 Pixmap format is rather verbose.
XYou may want to run large images through the \fIcompress\fP utility
Xbefore storing them in a file.  E.g.,
X.sp
X.ti +5
Xxgrabsc | compress >outfile.xpm.Z
X.sp
X.SH AUTHOR
X.nf
X+----------------------------+
X|     Bruce Schuchardt       |
X|    Servio Corporation      |
X|      bruce@slc.com         |
X+----------------------------+
X.fi
X.sp 2
X.SH ACKNOWLEGEMENTS
X.PP
XSome of the source code for xgrabsc came from
Xthe xloadimage project by Jim Frost (jimf@saber.com) and others.  Jim's
Xcopyright has been included both here and in the source code.
X.PP
XThe idea for using run-length encoding for Postscript output came from
Xthe xwd2ps project by Robert Tatar and Craig A. McGowan.
X.sp 2
X.SH COPYRIGHT
XCopyright (c) 1990 Bruce Schuchardt
X.sp
XHalftoning and Dithering code 
XCopyright (c) 1989, 1990 Jim Frost and others.
X.PP
X\fIXgrabsc\fR is copywritten material with a very loose copyright
Xallowing unlimited modification and distribution if the copyright
Xnotices are left intact.  Various portions are copywritten by various
Xpeople, but all use a modification of the MIT copyright notice.
XPlease check the source for complete copyright information.  The
Xintent is to keep the source free, not to stifle its distribution, so
Xplease write to me if you have any questions.
X.pp
XTHE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
XINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
XNO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
XCONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
XOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
XOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
XUSE OR PERFORMANCE OF THIS SOFTWARE.
X.s 2
X.SH SEE ALSO
XX(1X), xwud(1X), xwd2ps(1X), xloadimage(1X), xpm(1X), puzzle(1X),
Xcompress(1), uncompress(1)
END_OF_FILE
if test 7589 -ne `wc -c <'xgrabsc.man'`; then
    echo shar: \"'xgrabsc.man'\" unpacked with wrong size!
fi
# end of 'xgrabsc.man'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Bruce Schuchardt          Ph: (503) 629-8383
  Servio Logic              bruce@servio.SLC.COM
  Beaverton, OR             uunet!servio!bruce

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.