REPOST: v09i077: XV -- successor to XGIF, Part02/08
John Bradley
bradley at grip.cis.upenn.edu
Thu Oct 11 07:14:20 AEST 1990
Submitted-by: bradley at grip.cis.upenn.edu (John Bradley)
Posting-number: Volume 9, Issue 77
Archive-name: xv/part02
-------------------------(cut here)------------------
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./xvgifwr.c`
then
echo "writting ./xvgifwr.c"
cat > ./xvgifwr.c << '\BARFOO\'
/*
* xvgifwr.c - handles writing of GIF files. based on flgife.c and
* flgifc.c from the FBM Library, by Michael Maudlin
*
* Contains:
* WriteGIF(fp, pic, w, h, rmap, gmap, bmap, numcols, colorstyle)
*
* Note: slightly brain-damaged, in that it'll only write non-interlaced
* GIF files (in the interests of speed, or something)
*
*/
/*****************************************************************
* Portions of this code Copyright (C) 1989 by Michael Mauldin.
* Permission is granted to use this file in whole or in part provided
* that you do not sell it for profit and that this copyright notice
* and the names of all authors are retained unchanged.
*
* Authors: Michael Mauldin (mlm at cs.cmu.edu)
* David Rowley (mgardi at watdcsu.waterloo.edu)
*
* Based on: compress.c - File compression ala IEEE Computer, June 1984.
*
* Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
* Jim McKie (decvax!mcvax!jim)
* Steve Davies (decvax!vax135!petsd!peora!srd)
* Ken Turkowski (decvax!decwrl!turtlevax!ken)
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
*****************************************************************/
/*
* Copyright 1989, 1990 by the University of Pennsylvania
*
* Permission to use, copy, and distribute for non-commercial purposes,
* is hereby granted without fee, providing that the above copyright
* notice appear in all copies and that both the copyright notice and this
* permission notice appear in supporting documentation.
*
* The software may be modified for your own purposes, but modified versions
* may not be distributed.
*
* This software is provided "as is" without any express or implied warranty.
*/
#include "xv.h"
typedef long int count_int;
static int Width, Height;
static int curx, cury;
static long CountDown;
static int Interlace;
static byte bw[2] = {0, 0xff};
#ifdef __STDC__
static void putword(int, FILE *);
static void compress(int, FILE *, byte *, int);
static void output(int);
static void cl_block(void);
static void cl_hash(count_int);
static void char_init(void);
static void char_out(int);
static void flush_char(void);
#else
static void putword(), compress(), output(), cl_block(), cl_hash();
static void char_init(), char_out(), flush_char();
#endif
/*************************************************************/
int WriteGIF(fp, pic, w, h, rmap, gmap, bmap, numcols, colorstyle)
FILE *fp;
byte *pic;
int w,h;
byte *rmap, *gmap, *bmap;
int numcols, colorstyle;
{
int RWidth, RHeight;
int LeftOfs, TopOfs;
int Resolution, ColorMapSize, InitCodeSize, Background, BitsPerPixel;
int i,j;
/* if writing B/W stipple... */
if (colorstyle==2) {
rmap = gmap = bmap = bw;
numcols = 2;
}
Interlace = 0;
Background = 0;
/* figure out 'BitsPerPixel' */
for (i=1; i<8; i++)
if ( (1<<i) >= numcols) break;
BitsPerPixel = i;
ColorMapSize = 1 << BitsPerPixel;
RWidth = Width = w;
RHeight = Height = h;
LeftOfs = TopOfs = 0;
Resolution = BitsPerPixel;
CountDown = w * h; /* # of pixels we'll be doing */
if (BitsPerPixel <= 1) InitCodeSize = 2;
else InitCodeSize = BitsPerPixel;
curx = cury = 0;
if (!fp) {
fprintf(stderr, "WriteGIF: file not open for writing\n" );
return (1);
}
if (DEBUG)
fprintf(stderr,"WrGIF: pic=%lx, w,h=%dx%d, numcols=%d, Bits%d,Cmap=%d\n",
pic, w,h,numcols,BitsPerPixel,ColorMapSize);
fwrite("GIF87a", 1, 6, fp); /* the GIF magic number */
putword(RWidth, fp); /* screen descriptor */
putword(RHeight, fp);
i = 0x80; /* Yes, there is a color map */
i |= (8-1)<<4; /* OR in the color resolution (hardwired 8) */
i |= (BitsPerPixel - 1); /* OR in the # of bits per pixel */
fputc(i,fp);
fputc(Background, fp); /* background color */
fputc(0, fp); /* future expansion byte */
if (colorstyle == 1) { /* greyscale */
for (i=0; i<ColorMapSize; i++) {
j = MONO(rmap[i], gmap[i], bmap[i]);
fputc(j, fp);
fputc(j, fp);
fputc(j, fp);
}
}
else {
for (i=0; i<ColorMapSize; i++) { /* write out Global colormap */
fputc(rmap[i], fp);
fputc(gmap[i], fp);
fputc(bmap[i], fp);
}
}
fputc( ',', fp ); /* image separator */
/* Write the Image header */
putword(LeftOfs, fp);
putword(TopOfs, fp);
putword(Width, fp);
putword(Height, fp);
if (Interlace) fputc(0x40, fp); /* Use Global Colormap, maybe Interlace */
else fputc(0x00, fp);
fputc(InitCodeSize, fp);
compress(InitCodeSize+1, fp, pic, w*h);
fputc(0,fp); /* Write out a Zero-length packet (EOF) */
fputc(';',fp); /* Write GIF file terminator */
return (0);
}
/******************************/
static void putword(w, fp)
int w;
FILE *fp;
{
/* writes a 16-bit integer in GIF order (LSB first) */
fputc(w & 0xff, fp);
fputc((w>>8)&0xff, fp);
}
/***********************************************************************/
static unsigned long cur_accum = 0;
static int cur_bits = 0;
#define min(a,b) ((a>b) ? b : a)
#define BITS 12
#define MSDOS 1
#define HSIZE 5003 /* 80% occupancy */
typedef unsigned char char_type;
static int n_bits; /* number of bits/code */
static int maxbits = BITS; /* user settable max # bits/code */
static int maxcode; /* maximum code, given n_bits */
static int maxmaxcode = 1 << BITS; /* NEVER generate this */
#define MAXCODE(n_bits) ( (1 << (n_bits)) - 1)
static count_int htab [HSIZE];
static unsigned short codetab [HSIZE];
#define HashTabOf(i) htab[i]
#define CodeTabOf(i) codetab[i]
static int hsize = HSIZE; /* for dynamic table sizing */
/*
* To save much memory, we overlay the table used by compress() with those
* used by decompress(). The tab_prefix table is the same size and type
* as the codetab. The tab_suffix table needs 2**BITS characters. We
* get this from the beginning of htab. The output stack uses the rest
* of htab, and contains characters. There is plenty of room for any
* possible stack (stack used to be 8000 characters).
*/
#define tab_prefixof(i) CodeTabOf(i)
#define tab_suffixof(i) ((char_type *)(htab))[i]
#define de_stack ((char_type *)&tab_suffixof(1<<BITS))
static int free_ent = 0; /* first unused entry */
/*
* block compression parameters -- after all codes are used up,
* and compression rate changes, start over.
*/
static int clear_flg = 0;
static long int in_count = 1; /* length of input */
static long int out_count = 0; /* # of codes output (for
debugging) */
/*
* compress stdin to stdout
*
* Algorithm: use open addressing double hashing (no chaining) on the
* prefix code / next character combination. We do a variant of Knuth's
* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
* secondary probe. Here, the modular division first probe is gives way
* to a faster exclusive-or manipulation. Also do block compression with
* an adaptive reset, whereby the code table is cleared when the compression
* ratio decreases, but after the table fills. The variable-length output
* codes are re-sized at this point, and a special CLEAR code is generated
* for the decompressor. Late addition: construct the table according to
* file size for noticeable speed improvement on small files. Please direct
* questions about this implementation to ames!jaw.
*/
static int g_init_bits;
static FILE *g_outfile;
static int ClearCode;
static int EOFCode;
/********************************************************/
static void compress(init_bits, outfile, data, len)
int init_bits;
FILE *outfile;
byte *data;
int len;
{
register long fcode;
register int i = 0;
register int c;
register int ent;
register int disp;
register int hsize_reg;
register int hshift;
/*
* Set up the globals: g_init_bits - initial number of bits
* g_outfile - pointer to output file
*/
g_init_bits = init_bits;
g_outfile = outfile;
/* initialize 'compress' globals */
maxbits = BITS;
maxmaxcode = 1<<BITS;
memset((char *) htab, 0, sizeof(htab));
memset((char *) codetab, 0, sizeof(codetab));
hsize = HSIZE;
free_ent = 0;
clear_flg = 0;
in_count = 1;
out_count = 0;
cur_accum = 0;
cur_bits = 0;
/*
* Set up the necessary values
*/
out_count = 0;
clear_flg = 0;
in_count = 1;
maxcode = MAXCODE(n_bits = g_init_bits);
ClearCode = (1 << (init_bits - 1));
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
char_init();
ent = *data++; len--;
hshift = 0;
for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
hshift++;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash( (count_int) hsize_reg); /* clear hash table */
output(ClearCode);
while (len) {
c = *data++; len--;
in_count++;
fcode = (long) ( ( (long) c << maxbits) + ent);
i = (((int) c << hshift) ^ ent); /* xor hashing */
if ( HashTabOf (i) == fcode ) {
ent = CodeTabOf (i);
continue;
}
else if ( (long)HashTabOf (i) < 0 ) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if ( i == 0 )
disp = 1;
probe:
if ( (i -= disp) < 0 )
i += hsize_reg;
if ( HashTabOf (i) == fcode ) {
ent = CodeTabOf (i);
continue;
}
if ( (long)HashTabOf (i) > 0 )
goto probe;
nomatch:
output(ent);
out_count++;
ent = c;
if ( free_ent < maxmaxcode ) {
CodeTabOf (i) = free_ent++; /* code -> hashtable */
HashTabOf (i) = fcode;
}
else
cl_block();
}
/* Put out the final code */
output(ent);
out_count++;
output(EOFCode);
}
/*****************************************************************
* TAG( output )
*
* Output the given code.
* Inputs:
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
* that n_bits =< (long)wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
* Maintain a BITS character long buffer (so that 8 codes will
* fit in it exactly). Use the VAX insv instruction to insert each
* code in turn. When the buffer fills up empty it and start over.
*/
static
unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
0x001F, 0x003F, 0x007F, 0x00FF,
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
static void output(code)
int code;
{
cur_accum &= masks[cur_bits];
if (cur_bits > 0)
cur_accum |= ((long)code << cur_bits);
else
cur_accum = code;
cur_bits += n_bits;
while( cur_bits >= 8 ) {
char_out( (unsigned int) (cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if (free_ent > maxcode || clear_flg) {
if( clear_flg ) {
maxcode = MAXCODE (n_bits = g_init_bits);
clear_flg = 0;
}
else {
n_bits++;
if ( n_bits == maxbits )
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
if( code == EOFCode ) {
/* At EOF, write the rest of the buffer */
while( cur_bits > 0 ) {
char_out( (unsigned int)(cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char();
fflush( g_outfile );
if( ferror( g_outfile ) )
FatalError("unable to write GIF file");
}
}
/********************************/
static void cl_block () /* table clear for block compress */
{
/* Clear out the hash table */
cl_hash ( (count_int) hsize );
free_ent = ClearCode + 2;
clear_flg = 1;
output(ClearCode);
}
/********************************/
static void cl_hash(hsize) /* reset code table */
register count_int hsize;
{
register count_int *htab_p = htab+hsize;
register long i;
register long m1 = -1;
i = hsize - 16;
do { /* might use Sys V memset(3) here */
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p-9) = m1;
*(htab_p-8) = m1;
*(htab_p-7) = m1;
*(htab_p-6) = m1;
*(htab_p-5) = m1;
*(htab_p-4) = m1;
*(htab_p-3) = m1;
*(htab_p-2) = m1;
*(htab_p-1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for ( i += 16; i > 0; i-- )
*--htab_p = m1;
}
/******************************************************************************
*
* GIF Specific routines
*
***********************************************************************
********/
/*
* Number of characters so far in this 'packet'
*/
static int a_count;
/*
* Set up the 'byte output' routine
*/
static void char_init()
{
a_count = 0;
}
/*
* Define the storage for the packet accumulator
*/
static char accum[ 256 ];
/*
* Add a character to the end of the current packet, and if it is 254
* characters, flush the packet to disk.
*/
static void char_out(c)
int c;
{
accum[ a_count++ ] = c;
if( a_count >= 254 )
flush_char();
}
/*
* Flush the packet to disk, and reset the accumulator
*/
static void flush_char()
{
if( a_count > 0 ) {
fputc( a_count, g_outfile );
fwrite( accum, 1, a_count, g_outfile );
a_count = 0;
}
}
\BARFOO\
else
echo "will not over write ./xvgifwr.c"
fi
if `test ! -s ./xvinfo.c`
then
echo "writting ./xvinfo.c"
cat > ./xvinfo.c << '\BARFOO\'
/*
* xvinfo.c - 'Info' box handling functions
*
* callable functions:
*
* CreateInfo(geom) - creates the infoW window. Doesn't map it.
* InfoBox(vis) - random processing based on value of 'vis'
* maps/unmaps window, etc.
* RedrawInfo(x,y,w,h) - called by 'expose' events
* SetInfoMode(mode) - changes amount of info Info window shows
* SetISTR(st, fmt, args) - sprintf's into ISTR #st. Redraws it in window
* char *GetISTR(st) - returns pointer to ISTR #st, or NULL if st bogus
*/
/*
* Copyright 1989, 1990 by the University of Pennsylvania
*
* Permission to use, copy, and distribute for non-commercial purposes,
* is hereby granted without fee, providing that the above copyright
* notice appear in all copies and that both the copyright notice and this
* permission notice appear in supporting documentation.
*
* The software may be modified for your own purposes, but modified versions
* may not be distributed.
*
* This software is provided "as is" without any express or implied warranty.
*/
#define NEEDSVARARGS
#include "xv.h"
#include "bitmaps.h"
/* max length of an Info String */
#define ISTRLEN 80
/* baseline of top line of text */
#define TOPBASE (36 + penn_height/2 + 4 + 8 + ASCENT)
#define STLEFT 100 /* left edge of strings */
static Pixmap graspPix, pennPix;
static char istrs[NISTR][ISTRLEN];
#ifdef __STDC__
static void DrawStrings(void);
static void DrawFieldName(int);
static void RedrawString(int);
#else
static void DrawStrings(), DrawFieldName(), RedrawString();
#endif
/***************************************************/
void CreateInfo(geom)
char *geom;
{
infoW = CreateWindow("xv info", geom, INFOWIDE, INFOHIGH, infofg, infobg);
if (!infoW) FatalError("can't create info window!");
pennPix = XCreatePixmapFromBitmapData(theDisp, infoW, penn_bits, penn_width,
penn_height, infofg, infobg, dispDEEP);
graspPix = XCreatePixmapFromBitmapData(theDisp,infoW,grasp_bits,grasp_width,
grasp_height, infofg, infobg, dispDEEP);
}
/***************************************************/
void InfoBox(vis)
int vis;
{
if (vis) XMapRaised(theDisp, infoW);
else XUnmapWindow(theDisp, infoW);
infoUp = vis;
}
/***************************************************/
void RedrawInfo(x,y,w,h)
int x,y,w,h;
{
int i;
XSetForeground(theDisp, theGC, infofg);
XSetBackground(theDisp, theGC, infobg);
/* draw the two icons */
XCopyArea(theDisp, pennPix, infoW, theGC, 0, 0, penn_width, penn_height,
36 - penn_width/2, 36 - penn_height/2);
XCopyArea(theDisp, graspPix, infoW, theGC, 0, 0, grasp_width, grasp_height,
INFOWIDE - 36 - grasp_width/2, 36 - grasp_height/2);
/* draw the credits */
sprintf(str,"XV: The GIF Thing - %s",REVDATE);
CenterString(infoW, str, INFOWIDE/2, 36-LINEHIGH);
CenterString(infoW, "by John Bradley (bradley at cis.upenn.edu)"
, INFOWIDE/2, 36);
CenterString(infoW, "(c) 1990, University of Pennsylvania",
INFOWIDE/2, 36+LINEHIGH);
/* draw the dividing lines */
i = 36 + penn_height/2 + 4;
XDrawLine(theDisp, infoW, theGC, 0, i, INFOWIDE, i);
XDrawLine(theDisp, infoW, theGC, 0, i+2, INFOWIDE, i+2);
XDrawLine(theDisp, infoW, theGC, 0, INFOHIGH-20, INFOWIDE, INFOHIGH-20);
XDrawLine(theDisp, infoW, theGC, 0, INFOHIGH-22, INFOWIDE, INFOHIGH-22);
XDrawLine(theDisp, infoW, theGC, 0, INFOHIGH-40, INFOWIDE, INFOHIGH-40);
XDrawLine(theDisp, infoW, theGC, 0, INFOHIGH-42, INFOWIDE, INFOHIGH-42);
DrawStrings();
}
/***************************************************/
static void DrawStrings()
{
int i;
for (i=0; i<6; i++) DrawFieldName(i); /* draw the field titles */
for (i=0; i<NISTR; i++) RedrawString(i); /* draw the field values */
XFlush(theDisp);
}
/***************************************************/
static void DrawFieldName(fnum)
int fnum;
{
static char *fname[6] = { "Filename:", "Format:", "Resolution:",
"Cropping:", "Expansion:", "Colors:" };
XSetForeground(theDisp, theGC, infofg);
XSetBackground(theDisp, theGC, infobg);
if (infoMode == INF_NONE || infoMode == INF_STR) return;
if (infoMode == INF_PART && fnum>=3) return;
XDrawString(theDisp, infoW, theGC, 10, TOPBASE + fnum*LINEHIGH,
fname[fnum], strlen(fname[fnum]));
}
/***************************************************/
static void RedrawString(st)
int st;
{
/* erase area of string, and draw it with new contents */
XSetForeground(theDisp, theGC, infofg);
XSetBackground(theDisp, theGC, infobg);
if (infoMode == INF_NONE) return;
if (infoMode == INF_STR && st > ISTR_WARNING) return;
if (infoMode == INF_PART && st > ISTR_RES) return;
if (st == ISTR_INFO) {
XClearArea(theDisp, infoW, 0, INFOHIGH-19, INFOWIDE, 17, False);
CenterString(infoW, istrs[st], INFOWIDE/2, INFOHIGH-10);
}
else if (st == ISTR_WARNING) {
XClearArea(theDisp, infoW, 0, INFOHIGH-39, INFOWIDE, 17, False);
CenterString(infoW, istrs[st], INFOWIDE/2, INFOHIGH-30);
}
else {
XClearArea(theDisp,infoW,STLEFT,TOPBASE - ASCENT
+ (st-ISTR_FILENAME)*LINEHIGH, INFOWIDE-STLEFT, LINEHIGH,False);
XDrawString(theDisp, infoW, theGC, STLEFT,TOPBASE
+ (st-ISTR_FILENAME)*LINEHIGH, istrs[st], strlen(istrs[st]));
}
}
/***************************************************/
void SetInfoMode(mode)
int mode;
{
int y1, y2;
infoMode = mode;
if (infoUp) { /* only do this if window is mapped */
y1 = TOPBASE - ASCENT;
y2 = INFOHIGH-43;
XClearArea(theDisp, infoW, 0, y1, INFOWIDE, y2-y1, False);
XClearArea(theDisp, infoW, 0, INFOHIGH-39, INFOWIDE, 17, False);
XClearArea(theDisp, infoW, 0, INFOHIGH-19, INFOWIDE, 17, False);
DrawStrings();
}
}
/***************************************************/
/*VARARGS0*/
void SetISTR(va_alist)
va_dcl
{
va_list args;
char *fmt;
int stnum;
/* InfoStr( ISTR, format, arg1, arg2, ...) */
va_start(args);
stnum = va_arg(args, int);
if (stnum<0 || stnum>=NISTR) return;
fmt = va_arg(args, char *);
vsprintf(istrs[stnum], fmt, args);
va_end(args);
if (infoUp) {
RedrawString(stnum);
XFlush(theDisp);
}
if (ctrlUp && stnum == ISTR_INFO) {
DrawCtrlStr();
XFlush(theDisp);
}
}
/***************************************************/
char *GetISTR(stnum)
int stnum;
{
/* returns pointer to ISTR string */
if (stnum < 0 || stnum>=NISTR) return(NULL);
return (istrs[stnum]);
}
\BARFOO\
else
echo "will not over write ./xvinfo.c"
fi
if `test ! -s ./xvmisc.c`
then
echo "writting ./xvmisc.c"
cat > ./xvmisc.c << '\BARFOO\'
/*
* xvmisc.c - random routines that most of the work involved in putting
* a picture on an X display.
*
* Resize(w,h) - generates epic, new Ximage,
* SortColormap() - sorts the desired colormap into a 'most important color
* to allocate first' order
* AllocColors() - Takes the desired colormap and sees what it can do about
* getting it.
* DoCrop() - crops the picture
* UnCrop() - uncrops the picture (natch)
* Rotate() - rotates 'pic' 90-degrees clockwise
* FloydDitherize8 - Takes epic, and produces a b/w dithered version of it,
* stored in 8-bit per pixel format
* FloydDitherize1 - Does the same, but the output is an XYBitmap (1 bit per
* pixel, packed 8 pixels to a byte.
* CreateXImage - Given epic, generate an ximage of it. Handles displays
* of different depths.
* CenterString - civilized string drawing routine. uses font 'mfinfo'
* ULineString - draws underlined string. uses font 'mfinfo'
* StringWidth - civilized XTextWidth width interface, uses font 'mfinfo'
* FakeButtonPress - when a keyboard equiv is hit, fakes mouse click in button
* Timer - sleeps for a given # of milliseconds
*/
/*
* Copyright 1989, 1990 by the University of Pennsylvania
*
* Permission to use, copy, and distribute for non-commercial purposes,
* is hereby granted without fee, providing that the above copyright
* notice appear in all copies and that both the copyright notice and this
* permission notice appear in supporting documentation.
*
* The software may be modified for your own purposes, but modified versions
* may not be distributed.
*
* This software is provided "as is" without any express or implied warranty.
*/
#define NEEDSTIME
#include "xv.h"
#include "bitmaps.h"
#ifdef __STDC__
static void RotatePic(byte *, unsigned int *, unsigned int *);
static void FloydDitherize8(byte *);
static void FloydDitherize1(XImage *);
static void FreeMostResources(void);
#else
static void RotatePic(), FloydDitherize8(), FloydDitherize1();
static void FreeMostResources();
#endif
/***************************************************/
Window CreateWindow(name,geom,w,h,fg,bg)
char *name, *geom;
unsigned int w,h;
unsigned long fg, bg;
{
Window win;
XSetWindowAttributes xswa;
unsigned int xswamask;
XSizeHints hints;
int i,x,y;
/* note that only x,y are gotten from geom spec. w,h are fixed */
x = y = 1;
i = XParseGeometry(geom,&x,&y,&w,&h);
if (i&XValue || i&YValue) hints.flags = USPosition;
else hints.flags = PPosition;
hints.flags |= USSize;
if (i&XValue && i&XNegative) x = dispWIDE - w - abs(x);
if (i&YValue && i&YNegative) y = dispHIGH - h - abs(y);
hints.x = x; hints.y = y;
hints.width = w; hints.height = h;
hints.min_width = w; hints.min_height = h;
hints.max_width = w; hints.max_height = h;
hints.flags |= PMaxSize | PMinSize;
xswa.background_pixel = bg;
xswa.border_pixel = fg;
xswamask = CWBackPixel | CWBorderPixel;
win = XCreateWindow(theDisp, rootW, x, y, w, h,
bwidth, 0, CopyFromParent,
CopyFromParent, xswamask, &xswa);
if (!win) return(win); /* leave immediately if couldn't create */
XSetStandardProperties(theDisp, win, name, name, None, NULL, 0, &hints);
return(win);
}
/***********************************/
void Resize(w,h)
int w,h;
{
int cy,ex,ey,*cxarr, *cxarrp;
byte *clptr,*elptr,*epptr;
static char *rstr = "Resizing Image. Please wait...";
clptr = NULL; cxarrp = NULL; cy = 0; /* shut up compiler */
/* force w,h into valid ranges */
RANGE(w,1,dispWIDE); RANGE(h,1,dispHIGH);
SetISTR(ISTR_EXPAND, "%.3g x %.3g (%d x %d)",
((float) w) / cWIDE, ((float) h) / cHIGH, w, h);
/* if same size, and Ximage created, do nothing */
if (w==eWIDE && h==eHIGH && theImage!=NULL) return;
if (DEBUG) fprintf(stderr,"%s: Resize(%d,%d) eSIZE=%d,%d cSIZE=%d,%d\n",
cmd,w,h,eWIDE,eHIGH,cWIDE,cHIGH);
BTSetActive(&but[BCROP],0);
SetCropString();
if (w==cWIDE && h==cHIGH) { /* 1:1 expansion. point epic at cpic */
if (epic != cpic && epic!=NULL) free(epic);
epic = cpic; eWIDE = cWIDE; eHIGH = cHIGH;
}
else { /* have to actually SCALE THE PIC. Drats! */
StartFish();
WaitCursor();
/* if it's a big image, this could take a while. mention it */
if (w*h>(500*500) && !useroot) {
XSetForeground(theDisp, theGC, fg);
XSetBackground(theDisp, theGC, bg);
XClearWindow(theDisp,mainW);
XFlush(theDisp);
XDrawImageString(theDisp,mainW,theGC,CENTERX(mfinfo,w/2,rstr),
CENTERY(mfinfo,h/2),rstr, strlen(rstr));
XFlush(theDisp);
}
/* first, kill the old epic, if one exists */
if (epic!=NULL && epic!=cpic) {
free(epic); epic = NULL;
}
/* create a new epic of the appropriate size */
eWIDE = w; eHIGH = h;
epic = (byte *) malloc(w*h);
if (epic==NULL) {
sprintf(str,"unable to malloc a %dx%d image\n",w,h);
FatalError(str);
}
/* the scaling routine. not really all that scary after all... */
/* OPTIMIZATON IDEA. Malloc an eWIDE array of ints which will hold the
values of the equation px = (pWIDE * ex) / eWIDE. Faster than doing
a mul and a div for every point in picture */
cxarr = (int *) malloc(eWIDE * sizeof(int));
if (!cxarr) FatalError("unable to allocate cxarr");
for (ex=0; ex<eWIDE; ex++) cxarr[ex] = (cWIDE * ex) / eWIDE;
elptr = epptr = epic;
for (ey=0; ey<eHIGH; ey++, elptr+=eWIDE) {
if ((ey&127) == 0) WaitCursor();
cy = (cHIGH * ey) / eHIGH;
epptr = elptr;
clptr = cpic + (cy * cWIDE);
for (ex=0, cxarrp = cxarr; ex<eWIDE; ex++, epptr++)
*epptr = clptr[*cxarrp++];
}
free(cxarr);
}
/* now make something displayable out of epic */
CreateXImage();
StopFish();
}
/************************************************/
/* structure and routine used in SortColormap() */
/************************************************/
typedef struct thing
{ byte r,g,b;
int oldindex;
int use; } CMAPENT;
static int CMAPcompare(a,b)
CMAPENT *a,*b;
{
return (b->use - a->use);
}
/***********************************/
void SortColormap()
{
byte *p;
int i, j, k, mdist, entry, mn, d, hist[256], trans[256];
static CMAPENT c[256], c1[256], *cp, *cj, *ck;
/* no point doing this if we're on a 1-bit display */
if (ncols == 0) { numcols = 256; return; }
/* initialize histogram and compute it */
for (i=0; i<256; i++) hist[i]=0;
for (i=pWIDE*pHIGH, p=pic; i; i--, p++) hist[*p]++;
if (DEBUG>1) {
fprintf(stderr,"%s: Desired colormap\n",cmd);
for (i=0; i<256; i++)
if (hist[i]) fprintf(stderr,"(%3d %02x,%02x,%02x) ",
i,r[i],g[i],b[i]);
fprintf(stderr,"\n\n");
}
/* put the actually-used colors into the 'c' array in the order they occur */
/* also, while we're at it, calculate numcols */
for (i=numcols=0; i<256; i++) {
if (hist[i]) {
cp = &c[numcols++];
cp->r = r[i]; cp->g = g[i]; cp->b = b[i];
cp->use = hist[i]; cp->oldindex = i;
}
}
/* find most-used color, put that in c1[0] */
entry = -1; mdist = -1;
for (i=0; i<numcols; i++) {
if (c[i].use > mdist) { mdist = c[i].use; entry=i; }
}
memcpy(&c1[0], &c[entry], sizeof(CMAPENT));
c[entry].use = 0; /* and mark it dealt with */
/* sort rest of colormap, in order of decreasing 'distance' from already
allocated elements.
FURTHER MODIFICATION of algorithm. The algorithm's performance
utterly goes to hell as numcols increases. (Probably on the order
of O^3 performance). Since I don't see a clever way of rewriting
the algorithm for O^2 performance (which'd be acceptable), I'm going
to make a trade-off. I'll only run the algorithm for the first 32 colors
(or so). It can do that Real Fast. Then I'll just stick the rest of
the unsorted colors (if any), and tack them on the end, in order of
amount of use. This should give similar picture quality, with
much higher performance. */
for (i=1; i<numcols && i<32; i++) {
/* find the i'th most different color */
entry = -1; mdist = -1;
for (j=0, cj=c; j<numcols; j++,cj++) {
if (cj->use) { /* this color has not been marked already */
mn = 10000;
for (k=0, ck=c1; k<i; k++,ck++) {
d = abs(cj->r - ck->r) + abs(cj->g - ck->g) + abs(cj->b - ck->b);
if (mn>d) mn=d;
}
/* mn = minimum distance from c[j] to already used colors */
/* we want to select the unused color that has the greatest mn */
if (mn > mdist) { mdist = mn; entry = j; }
}
}
/* c[entry] is the next color to put in the map. do so */
memcpy(&c1[i], &c[entry], sizeof(CMAPENT));
c[entry].use = 0;
}
/* tack rest of colors onto colormap in decreasing order of use */
qsort((char *) c,numcols,sizeof(CMAPENT),CMAPcompare);
memcpy(&c1[i], c, (numcols - i) * sizeof(CMAPENT));
/* build translation table */
for (i=0; i<numcols; i++) trans[ c1[i].oldindex ] = i;
/* modify 'pic' to reflect the new colormap */
for (i=pWIDE*pHIGH, p=pic; i; i--, p++) *p = trans[*p];
/* and copy the new colormap into *the* colormap */
for (i=0; i<numcols; i++) {
r[i] = c1[i].r; g[i] = c1[i].g; b[i] = c1[i].b;
}
if (DEBUG>1) {
fprintf(stderr,"%s: result of sorting colormap\n",cmd);
for (i=0; i<numcols; i++)
fprintf(stderr,"(%3d %02x,%02x,%02x) ",i,r[i],g[i],b[i]);
fprintf(stderr,"\n\n");
fprintf(stderr,"%s: translate table\n",cmd);
for (i=0; i<numcols; i++)
fprintf(stderr,"%3d->%3d ",i,trans[i]);
fprintf(stderr,"\n\n");
}
}
#define NOPIX 0xffffffff
/***********************************/
void AllocColors()
{
int i, j, unique, p2alloc, p3alloc;
Colormap cmap;
XColor defs[256];
XColor ctab[256];
int dc;
nfcols = unique = p2alloc = p3alloc = 0;
rwthistime = 0;
if (ncols == 0) {
SetISTR(ISTR_COLOR,"no colors allocated. Using black & white.");
SetISTR(ISTR_COLOR2,"");
return;
}
/* FIRST PASS COLOR ALLOCATION:
for each color in the 'desired colormap', try to get it via
XAllocColor(). If for any reason it fails, mark that pixel
'unallocated' and worry about it later. Repeat. */
/* attempt to allocate first ncols entries in colormap
note: On displays with less than 8 bits per RGB gun, it's quite
possible that different colors in the original picture will be
mapped to the same color on the screen. X does this for you
silently. However, this is not-desirable for this application,
because when I say 'allocate me 32 colors' I want it to allocate
32 different colors, not 32 instances of the same 4 shades... */
for (i=0; i<numcols; i++) cols[i] = NOPIX;
cmap = theCmap;
for (i=0; i<numcols && unique<ncols; i++) {
defs[i].red = r[i]<<8;
defs[i].green = g[i]<<8;
defs[i].blue = b[i]<<8;
defs[i].flags = DoRed | DoGreen | DoBlue;
if (XAllocColor(theDisp, cmap, &defs[i])) {
unsigned long pixel, *fcptr;
pixel = cols[i] = defs[i].pixel;
/* see if the newly allocated color is new and different */
for (j=0, fcptr=freecols; j<nfcols && *fcptr!=pixel; j++,fcptr++);
if (j==nfcols) unique++;
fc2pcol[nfcols] = i;
freecols[nfcols++] = pixel;
}
else {
/* the allocation failed. If we want 'perfect' color, and we haven't
already created our own colormap, we'll want to do so */
if (perfect && !LocalCmap) {
LocalCmap = XCopyColormapAndFree(theDisp,theCmap);
XSetWindowColormap(theDisp,mainW, LocalCmap);
cmap = LocalCmap;
i--; /* redo the allocation request */
}
else
/* either we don't care about perfect color, or we do care, have
allocated our own colormap, and have STILL run out of colors
(possible, even on an 8 bit display), just mark pixel as
unallocated. We'll deal with it later */
cols[i] = NOPIX;
}
} /* FIRST PASS */
if (nfcols==numcols) {
if (numcols != unique)
SetISTR(ISTR_COLOR,"Got all %d desired colors. (%d unique)", numcols,
unique);
else
SetISTR(ISTR_COLOR,"Got all %d desired colors.", numcols);
SetISTR(ISTR_COLOR2,"");
return;
}
/* SECOND PASS COLOR ALLOCATION:
Allocating 'exact' colors failed. Now try to allocate 'closest'
colors.
Read entire X colormap (or first 256 entries) in from display.
for each unallocated pixel, find the closest color that actually
is in the X colormap. Try to allocate that color (read only).
If that fails, the THIRD PASS will deal with it */
SetISTR(ISTR_COLOR,"Got %d out of %d colors. (%d unique)",
nfcols,numcols,unique);
/* read entire colormap (or first 256 entries) into 'ctab' */
dc = (ncells<256) ? ncells : 256;
for (i=0; i<dc; i++) ctab[i].pixel = (unsigned long) i;
XQueryColors(theDisp, cmap, ctab, dc);
for (i=0; i<numcols && unique<ncols; i++)
if (cols[i]==NOPIX) { /* an unallocated pixel */
int d, mdist, close;
unsigned long ri,gi,bi;
mdist = 100000; close = -1;
ri = r[i]; gi = g[i]; bi = b[i];
for (j=0; j<dc; j++) {
d = abs(ri - (ctab[j].red>>8)) +
abs(gi - (ctab[j].green>>8)) +
abs(bi - (ctab[j].blue>>8));
if (d<mdist) { mdist=d; close=j; }
}
if (close<0) FatalError("This Can't Happen! (How reassuring.)");
if (XAllocColor(theDisp, cmap, &ctab[close])) {
memcpy(&defs[i], &ctab[close], sizeof(XColor));
cols[i] = ctab[close].pixel;
fc2pcol[nfcols] = i;
freecols[nfcols++] = cols[i];
p2alloc++;
unique++;
}
}
/* THIRD PASS COLOR ALLOCATION:
We've alloc'ed all the colors we can. Now, we have to map any
remaining unalloced pixels into either A) the colors that we DID get
(noglob), or B) the colors found in the X colormap */
for (i=0; i<numcols; i++) {
if (cols[i] == NOPIX) { /* an unallocated pixel */
int d, k, mdist, close;
unsigned long ri,gi,bi;
mdist = 100000; close = -1;
ri = r[i]; gi = g[i]; bi = b[i];
if (!noglob) { /* search the entire X colormap */
for (j=0; j<dc; j++) {
d = abs(ri - (ctab[j].red>>8)) +
abs(gi - (ctab[j].green>>8)) +
abs(bi - (ctab[j].blue>>8));
if (d<mdist) { mdist=d; close=j; }
}
if (close<0) FatalError("This Can't Happen! (How reassuring.)");
memcpy(&defs[i], &ctab[close], sizeof(XColor));
cols[i] = defs[i].pixel;
p3alloc++;
}
else { /* only search the alloc'd colors */
for (j=0; j<nfcols; j++) {
k = fc2pcol[j];
d = abs(ri - (defs[k].red>>8)) +
abs(gi - (defs[k].green>>8)) +
abs(bi - (defs[k].blue>>8));
if (d<mdist) { mdist=d; close=k; }
}
if (close<0) FatalError("This Can't Happen! (How reassuring.)");
memcpy(&defs[i], &defs[close], sizeof(XColor));
cols[i] = defs[i].pixel;
}
}
} /* THIRD PASS */
if (p2alloc && p3alloc)
SetISTR(ISTR_COLOR2,"Got %d 'close' color%s. 'Borrowed' %d color%s.",
p2alloc, (p2alloc>1) ? "s" : "",
p3alloc, (p3alloc>1) ? "s" : "");
else if (p2alloc && !p3alloc)
SetISTR(ISTR_COLOR2,"Got %d 'close' color%s.",
p2alloc, (p2alloc>1) ? "s" : "");
else if (!p2alloc && p3alloc)
SetISTR(ISTR_COLOR2,"'Borrowed' %d color%s.",
p3alloc, (p3alloc>1) ? "s" : "");
}
/***********************************/
void AllocRWColors()
{
int i,j;
Colormap cmap;
XColor defs[256];
nfcols = 0; rwthistime = 1;
if (ncols == 0) {
SetISTR(ISTR_COLOR,"no colors allocated. Using black & white.");
SetISTR(ISTR_COLOR2,"");
rwthistime = 0;
return;
}
cmap = theCmap;
for (i=0; i<numcols; i++) cols[i] = NOPIX;
for (i=0; i<numcols && i<ncols; i++) {
unsigned long pmr[1], pix[1];
if (XAllocColorCells(theDisp, cmap, False, pmr, 0, pix, 1)) {
defs[i].pixel = cols[i] = pix[0];
defs[i].red = r[i]<<8;
defs[i].green = g[i]<<8;
defs[i].blue = b[i]<<8;
defs[i].flags = DoRed | DoGreen | DoBlue;
fc2pcol[nfcols] = i;
freecols[nfcols++] = pix[0];
}
else {
if (perfect && !LocalCmap) {
LocalCmap = XCopyColormapAndFree(theDisp,theCmap);
XSetWindowColormap(theDisp,mainW, LocalCmap);
cmap = LocalCmap;
i--; /* redo the allocation request */
}
else cols[i] = NOPIX;
}
} /* for (i=0; ... */
if (nfcols==numcols) {
SetISTR(ISTR_COLOR,"Got all %d desired colors.", numcols);
SetISTR(ISTR_COLOR2,"");
}
else {
/* Failed to allocate all colors in picture. Map remaining desired
colors into closest allocated desired colors */
if (nfcols==0) {
SetISTR(ISTR_COLOR,"No r/w cells available. Using r/o color.");
AllocColors();
return;
}
SetISTR(ISTR_COLOR,"Got %d out of %d colors.", nfcols,numcols);
for (i=0; i<numcols; i++)
if (cols[i]==NOPIX) { /* an unallocated pixel */
int k, d, mdist, close;
unsigned long ri,gi,bi;
mdist = 100000; close = -1;
ri = r[i]; gi = g[i]; bi = b[i];
for (j=0; j<nfcols; j++) {
k = fc2pcol[j];
d = abs(ri - (defs[k].red>>8)) + abs(gi - (defs[k].green>>8)) +
abs(bi - (defs[k].blue>>8));
if (d<mdist) { mdist=d; close=k; }
}
if (close<0) FatalError("This Can't Happen! (How reassuring.)");
cols[i] = defs[close].pixel;
}
}
/* load up the allocated colorcells */
for (i=0; i<nfcols; i++) {
j = fc2pcol[i];
defs[i].pixel = freecols[i];
defs[i].red = r[j]<<8;
defs[i].green = g[j]<<8;
defs[i].blue = b[j]<<8;
defs[i].flags = DoRed | DoGreen | DoBlue;
/* fprintf(stderr,"StoreColors: %3d = %3d,%3d,%3d\n",
defs[i].pixel,r[j],g[j],b[j]); */
}
XStoreColors(theDisp, cmap, defs, nfcols);
XStoreColor(theDisp, cmap, &defs[0]); /* bug in XStoreColors call */
}
/***********************************/
void DoMonoAndRV()
{
int i;
/* operate on original colors, before any gamma correction */
for (i=0; i<numcols; i++) {
r[i] = rorg[i]; g[i] = gorg[i]; b[i] = borg[i];
}
if (mono || ncols==0) /* if monochrome, mono-ify the desired colormap */
for (i=0; i<numcols; i++)
r[i] = g[i] = b[i] = MONO(r[i],g[i],b[i]);
if (revvideo) /* reverse the desired colormaps */
for (i=0; i<numcols; i++) {
r[i] = 255-r[i]; g[i] = 255-g[i]; b[i] = 255-b[i];
}
}
/***********************************/
void DoCrop()
{
int i,j;
byte *cp, *pp;
if (!but[BCROP].active) return;
/* turn off the cropping rectangle */
InvCropRect(); BTSetActive(&but[BCROP],0);
/* sort crx1,crx2,cry1,cry2 so that crx1,cry1 are top left corner */
if (crx1>crx2) { i = crx1; crx1 = crx2; crx2 = i; }
if (cry1>cry2) { i = cry1; cry1 = cry2; cry2 = i; }
/* see if cropping to same size, in which case do nothing */
if (crx2-crx1 == eWIDE && cry2-cry1 == eHIGH) return;
/* dispose of old cpic and epic */
if (epic && epic != cpic) free(epic);
if (cpic && cpic != pic) free(cpic);
epic = cpic = NULL;
/* figure out what the crop rectangles coordinates are in pic coordinates */
cXOFF = cXOFF + (crx1 * cWIDE) / eWIDE;
cYOFF = cYOFF + (cry1 * cHIGH) / eHIGH;
cWIDE = ((crx2 - crx1) * cWIDE) / eWIDE;
cHIGH = ((cry2 - cry1) * cHIGH) / eHIGH;
if (cWIDE<1) cWIDE = 1;
if (cHIGH<1) cHIGH = 1;
if (DEBUG) fprintf(stderr,"%s: cropping to %dx%d rectangle at %d,%d\n",
cmd, cWIDE, cHIGH, cXOFF, cYOFF);
/* kill old Ximage so that Resize will be forced to generate a new one */
if (theImage != NULL) XDestroyImage(theImage);
theImage = NULL;
/* at this point, we want to generate cpic, which will contain a
cWIDE*cHIGH subsection of 'pic', top-left at cXOFF,cYOFF */
cpic = (byte *) malloc(cWIDE * cHIGH);
if (cpic == NULL) {
fprintf(stderr,"%s: unable to allocate memory for cropped image\n", cmd);
WUnCrop();
cpic = pic; cXOFF = cYOFF = 0; cWIDE = pWIDE; cHIGH = pHIGH;
SetCropString();
return;
}
/* copy relevant pixels from pic to cpic */
cp = cpic;
for (i=0; i<cHIGH; i++) {
pp = pic + (i+cYOFF) * pWIDE + cXOFF;
for (j=0; j<cWIDE; j++)
*cp++ = *pp++;
}
SetCropString();
BTSetActive(&but[BUNCROP],1);
/* make new window same size as crop rect */
WCrop(crx2-crx1,cry2-cry1);
}
/***********************************/
void UnCrop()
{
int w,h;
if (cpic == pic) return; /* not cropped */
BTSetActive(&but[BUNCROP],0);
/* dispose of old cpic and epic */
if (epic && epic != cpic) free(epic);
if (cpic && cpic != pic) free(cpic);
epic = cpic = NULL;
/* kill old Ximage so that Resize will be forced to generate a new one */
if (theImage != NULL) XDestroyImage(theImage);
theImage = NULL;
w = (pWIDE * eWIDE) / cWIDE;
h = (pHIGH * eHIGH) / cHIGH;
RANGE(w,1,dispWIDE); RANGE(h,1,dispHIGH);
WUnCrop();
cpic = pic; cXOFF = cYOFF = 0; cWIDE = pWIDE; cHIGH = pHIGH;
SetCropString();
}
/***********************************/
void Rotate()
{
int i;
RotatePic(pic, &pWIDE, &pHIGH);
/* rotate clipped version and modify 'clip' coords */
if (cpic != pic && cpic != NULL) {
i = pWIDE - (cYOFF + cHIGH); /* have to rotate offsets */
cYOFF = cXOFF;
cXOFF = i;
RotatePic(cpic, &cWIDE, &cHIGH);
}
else { cWIDE = pWIDE; cHIGH = pHIGH; }
/* rotate expanded version */
if (epic != cpic && epic != NULL) {
RotatePic(epic, &eWIDE, &eHIGH);
}
else { eWIDE = cWIDE; eHIGH = cHIGH; }
CreateXImage();
WRotate();
}
/************************/
static void RotatePic(pic, wp, hp)
byte *pic;
unsigned int *wp, *hp;
{
/* rotates a w*h array of bytes 90 deg clockwise. swaps w and h */
byte *pic1, *pix1, *pix;
int i,j;
unsigned int w,h;
w = *wp; h = *hp;
pix1 = pic1 = (byte *) malloc(w*h);
if (!pic1) FatalError("Not enough memory to rotate!");
/* do the rotation */
for (i=0; i<w; i++)
for (j=h-1, pix=pic+(h-1)*w + i; j>=0; j--, pix1++, pix-=w)
*pix1 = *pix;
/* copy the rotated buffer into the original buffer */
memcpy(pic, pic1, w*h);
free(pic1);
/* swap w and h */
*wp = h; *hp = w;
}
/************************/
static void FloydDitherize8(image)
byte *image;
{
/* takes epic, and builds a black&white dithered version of it.
stores result in 8bit Pixmap format in 'image' */
int i;
byte *p;
FSDither(epic, eWIDE, eHIGH, image);
/* set to 'black' and 'white' instead of '0' and '1' */
if (black != 0 || white != 1) {
for (i=eWIDE*eHIGH, p=image; i>0; i--, p++) {
if (*p) *p = white; else *p = black;
}
}
}
/************************/
static void FloydDitherize1(ximage)
XImage *ximage;
{
/* same as FloydDitherize8, but output is a 1-bit per pixel XYBitmap,
packed 8 pixels per byte */
register short *dp;
register byte pix8, bit;
short *dithpic;
int i, j, err, bperln, order;
byte *pp, *image, w, b, w8, b8;
image = (byte *) theImage->data;
bperln = theImage->bytes_per_line;
order = theImage->bitmap_bit_order;
if (DEBUG) fprintf(stderr,"Ditherizing1...");
dithpic = (short *) malloc(eWIDE * eHIGH * sizeof(short));
if (dithpic == NULL) FatalError("not enough memory to ditherize");
w = white&0x1; b=black&0x1;
w8 = w<<7; b8 = b<<7; /* b/w bit in high bit */
/* copy r[epic] into dithpic so that we can run the algorithm */
pp = epic; dp = dithpic;
for (i=eHIGH * eWIDE; i>0; i--) *dp++ = fsgamcr[r[*pp++]];
dp = dithpic;
pp = image;
for (i=0; i<eHIGH; i++) {
pp = image + i*bperln;
if (order==LSBFirst) {
bit = pix8 = 0;
for (j=0; j<eWIDE; j++,dp++) {
if (*dp<128) { err = *dp; pix8 |= b8; }
else { err = *dp-255; pix8 |= w8; }
if (bit==7) {
*pp++ = pix8; bit=pix8=0;
}
else { pix8 >>= 1; bit++; }
if (j<eWIDE-1) dp[1] += ((err*7)/16);
if (i<eHIGH-1) {
dp[eWIDE] += ((err*5)/16);
if (j>0) dp[eWIDE-1] += ((err*3)/16);
if (j<eWIDE-1) dp[eWIDE+1] += (err/16);
}
}
if (bit) *pp++ = pix8>>(7-bit); /* write partial byte at end of line */
}
else { /* order==MSBFirst */
bit = pix8 = 0;
for (j=0; j<eWIDE; j++,dp++) {
if (*dp<128) { err = *dp; pix8 |= b; }
else { err = *dp-255; pix8 |= w; }
if (bit==7) {
*pp++ = pix8; bit=pix8=0;
}
else { pix8 <<= 1; bit++; }
if (j<eWIDE-1) dp[1] += ((err*7)/16);
if (i<eHIGH-1) {
dp[eWIDE] += ((err*5)/16);
if (j>0) dp[eWIDE-1] += ((err*3)/16);
if (j<eWIDE-1) dp[eWIDE+1] += (err/16);
}
}
if (bit) *pp++ = pix8<<(7-bit); /* write partial byte at end of line */
}
}
if (DEBUG) fprintf(stderr,"done\n");
free(dithpic);
}
/************************/
void FSDither(inpic, w, h, outpic)
byte *inpic, *outpic;
int w,h;
{
/* takes inpic, and builds a black&white dithered version of it.
stores result as 1 byte per pixel format in 'image'
black = 0; white = 1;
temporarily mallocs a w*h array of SHORTS.
(need to be signed, also to avoid overflow problems.) */
/* floyd-steinberg dithering.
*
* ---- x 7/16
* 3/16 5/16 1/16
*
*/
short *dp, *dithpic;
int i, j, err, w1, h1;
byte *pp, rgb[256];
if (DEBUG) fprintf(stderr,"Ditherizing...");
/* first thing to do is build rgb[], which will hold the B/W intensity
of the colors in the r,g,b arrays */
for (i=0; i<256; i++)
rgb[i] = MONO(r[i], g[i], b[i]);
dithpic = (short *) malloc(w*h * sizeof(short));
if (dithpic == NULL) FatalError("not enough memory to ditherize");
w1 = w-1; h1 = h-1;
/* copy rgb[inpic] into dithpic so that we can run the algorithm */
pp = inpic; dp = dithpic;
for (i=w*h; i>0; i--) *dp++ = fsgamcr[rgb[*pp++]];
dp = dithpic; pp = outpic;
for (i=0; i<h; i++)
for (j=0; j<w; j++,dp++,pp++) {
if (*dp<128) { err = *dp; *pp = 0; }
else { err = *dp-255; *pp = 1; }
if (j<w1) dp[1] += ((err*7)/16);
if (i<h1) {
dp[w] += ((err*5)/16);
if (j>0) dp[w1] += ((err*3)/16);
if (j<w1) dp[w+1] += (err/16);
}
}
if (DEBUG) fprintf(stderr,"done\n");
free(dithpic);
}
/***********************************/
void CreateXImage()
{
int i;
/*
* this has to do the tricky bit of converting the data in 'epic'
* into something usable for X.
*
* Algorithm notes:
* if dispDEEP is 8, nothing has to be done other than create an
* Ximage (ZPixmap, depth=8) and point it at the 'epic' data.
*
* if dispDEEP is 1, format'll be an XYBitmap, special case code
*
* if dispDEEP is 4, format'll be a ZPixmap, 4 or 8 bits per pixel
*
* if dispDEEP is 6, format'll be a ZPixmap, 8 bits per pixel
*
* if dispDEEP is 24, format'll be a ZPixmap. 32 bits per pixel
*
* any other value of dispDEEP will use a XYPixmap of the appropriate
* depth, and some slug-like general-case code DOESN'T YET!!
*/
if (DEBUG)
fprintf(stderr,"%s: creating a %dx%d Ximage, %d bits deep\n",
cmd, eWIDE, eHIGH, dispDEEP);
/* destroy old image and imagedata, if there is one */
if (theImage) XDestroyImage(theImage);
theImage = NULL;
switch (dispDEEP)
{
case 8:
{
byte *imagedata, *ip, *pp;
imagedata = (byte *) malloc(eWIDE*eHIGH);
if (!imagedata) FatalError("couldn't malloc imagedata");
if (ncols==0) FloydDitherize8(imagedata);
else {
for (i=eWIDE*eHIGH, pp=epic, ip=imagedata; i>0; i--,pp++,ip++) {
if ((i&0x1ffff) == 0) WaitCursor();
*ip = (byte) cols[*pp];
}
}
theImage = XCreateImage(theDisp,theVisual,dispDEEP,ZPixmap,0,
(char *) imagedata, eWIDE, eHIGH, 8, 0);
if (!theImage) FatalError("couldn't create theImage!");
}
break;
/*********************************/
case 1:
{
byte *imagedata;
theImage = XCreateImage(theDisp, theVisual, dispDEEP, XYPixmap, 0, NULL,
eWIDE, eHIGH, 8, 0);
if (!theImage) FatalError("couldn't create theImage!");
imagedata = (byte *) malloc(theImage->bytes_per_line * eHIGH);
if (!imagedata) FatalError("couldn't malloc imagedata");
theImage->data = (char *) imagedata;
FloydDitherize1(theImage);
}
break;
/*********************************/
case 4: {
byte *imagedata, *ip, *pp;
byte *lip;
int bperline, half, j;
theImage = XCreateImage(theDisp, theVisual, dispDEEP, ZPixmap, 0, NULL,
eWIDE, eHIGH, 8, 0);
if (!theImage) FatalError("couldn't create theImage!");
bperline = theImage->bytes_per_line;
imagedata = (byte *) malloc(bperline * eHIGH);
if (!imagedata) FatalError("couldn't malloc imagedata");
theImage->data = (char *) imagedata;
if (ncols==0) { /* ditherize */
byte *dith;
dith = (byte *) malloc(eWIDE * eHIGH);
if (!dith) FatalError("can't create dithered image");
FloydDitherize8(dith);
if (theImage->bits_per_pixel == 4) {
for (i=0, pp=dith, lip=imagedata; i<eHIGH; i++, lip+=bperline)
for (j=0, ip=lip, half=0; j<eWIDE; j++,pp++,half++) {
if (half&1) { *ip = *ip + ((*pp&0x0f)<<4); ip++; }
else *ip = *pp&0x0f;
}
}
else if (theImage->bits_per_pixel == 8)
memcpy(imagedata, dith, eWIDE*eHIGH);
else FatalError("This display is too bizarre. Can't create XImage.");
free(dith);
}
else { /* don't ditherize */
if (theImage->bits_per_pixel == 4) {
for (i=0, pp=epic, lip=imagedata; i<eHIGH; i++, lip+=bperline)
for (j=0, ip=lip, half=0; j<eWIDE; j++,pp++,half++) {
if (half&1) { *ip = *ip + ((cols[*pp]&0x0f)<<4); ip++; }
else *ip = cols[*pp]&0x0f;
}
}
else if (theImage->bits_per_pixel == 8) {
for (i=eWIDE*eHIGH, pp=epic, ip=imagedata; i>0; i--,pp++,ip++)
*ip = (byte) cols[*pp];
}
else FatalError("This display's too bizarre. Can't create XImage.");
}
}
break;
/*********************************/
case 6: {
byte *imagedata, *ip, *pp;
int bperline;
theImage = XCreateImage(theDisp, theVisual, dispDEEP, ZPixmap, 0, NULL,
eWIDE, eHIGH, 8, 0);
if (!theImage) FatalError("couldn't create theImage!");
if (theImage->bits_per_pixel != 8)
FatalError("This display's too bizarre. Can't create XImage.");
bperline = theImage->bytes_per_line;
imagedata = (byte *) malloc(bperline * eHIGH);
if (!imagedata) FatalError("couldn't malloc imagedata");
theImage->data = (char *) imagedata;
if (ncols==0) FloydDitherize8(imagedata);
else {
for (i=eWIDE*eHIGH, pp=epic, ip=imagedata; i>0; i--,pp++,ip++)
*ip = (byte) cols[*pp];
}
}
break;
/*********************************/
case 24:
{
byte *imagedata, *ip, *pp;
imagedata = (byte *) malloc(4*eWIDE*eHIGH);
if (!imagedata) FatalError("couldn't malloc imagedata");
theImage = XCreateImage(theDisp,theVisual,dispDEEP,ZPixmap,0,
(char *) imagedata, eWIDE, eHIGH, 32, 0);
if (!theImage) FatalError("couldn't create theImage!");
if (theImage->byte_order == MSBFirst)
for (i=eWIDE*eHIGH, pp=epic, ip=imagedata; i>0; i--,pp++) {
*ip++ = 0;
*ip++ = (cols[*pp]>>16) & 0xff;
*ip++ = (cols[*pp]>>8) & 0xff;
*ip++ = cols[*pp] & 0xff;
}
else
for (i=eWIDE*eHIGH, pp=epic, ip=imagedata; i>0; i--,pp++) {
*ip++ = cols[*pp] & 0xff;
*ip++ = (cols[*pp]>>8) & 0xff;
*ip++ = (cols[*pp]>>16) & 0xff;
*ip++ = 0;
}
}
break;
/*********************************/
default:
sprintf(str,"no code to handle this display type (%d bits deep)",
dispDEEP);
FatalError(str);
break;
}
}
/**************************************************/
void CenterString(win,str,x,y)
Window win;
char *str;
int x,y;
{
XDrawString(theDisp, win, theGC, CENTERX(mfinfo, x, str),
CENTERY(mfinfo, y), str, strlen(str));
}
/**************************************************/
void ULineString(win,str,x,y)
Window win;
char *str;
int x,y;
{
XDrawString(theDisp, win, theGC, x, y, str, strlen(str));
XDrawLine(theDisp, win, theGC, x, y+DESCENT-1,
x+StringWidth(str), y+DESCENT-1);
}
/**************************************************/
int StringWidth(str)
char *str;
{
return(XTextWidth(mfinfo, str, strlen(str)));
}
/***********************************/
void FakeButtonPress(bp)
BUTT *bp;
{
/* called when a button keyboard equivalent has been pressed.
'fakes' a ButtonPress event in the button, which A) makes the button
blink, and B) falls through to ButtonPress command dispatch code */
XButtonEvent ev;
ev.type = ButtonPress;
ev.send_event = True;
ev.display = theDisp;
ev.window = bp->win;
ev.root = rootW;
ev.subwindow = NULL;
ev.x = bp->x;
ev.y = bp->y;
ev.state = 0;
ev.button = Button1;
XSendEvent(theDisp, bp->win, False, NoEventMask, (XEvent *) &ev);
}
/**************************************************/
void SetCropString()
{
/* sets the crop string in the info box to be correct. should
be called whenever 'but[BCROP].active', cXOFF,cYOFF,cWIDE,cHIGH
are changed */
/* if but[BCROP].active, there's a cropping rectangle drawn on the picture.
the string should reflect that. */
if (but[BCROP].active) {
/* figure out current cropping rectangle in 'pic' coordinates */
int x,y,w,h;
int cx,cy,dx,dy;
if (crx1<crx2) cx=crx1; else cx=crx2;
if (cry1<cry2) cy=cry1; else cy=cry2;
dx = abs(crx1-crx2); dy = abs(cry1-cry2);
x = cXOFF + (cx * cWIDE) / eWIDE;
y = cYOFF + (cy * cHIGH) / eHIGH;
w = (dx * cWIDE) / eWIDE;
h = (dy * cHIGH) / eHIGH;
SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d", w, h, x, y);
}
else { /* cropping rectangle is turned off */
if (cpic != pic)
SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d",
cWIDE, cHIGH, cXOFF, cYOFF);
else
SetISTR(ISTR_CROP, "none");
}
}
/***********************************/
void Warning()
{
char *st;
int i;
if (infoUp || ctrlUp) { /* give 'em time to read message */
if (!fishrunning) sleep(5);
else {
for (i=0; i<25; i++) sigpause(0);
}
}
else {
st = GetISTR(ISTR_INFO);
fprintf(stderr,"%s: %s\n", cmd, st);
}
}
/***********************************/
void FatalError (identifier)
char *identifier;
{
fprintf(stderr, "%s: %s\n",cmd, identifier);
Quit(-1);
}
/***********************************/
void Quit(i)
int i;
{
FreeMostResources();
exit(i);
}
/***********************************/
static void FreeMostResources()
{
/* called when the program exits. frees everything explictly created
EXCEPT allocated colors. This is used when 'useroot' is in operation,
as we have to keep the alloc'd colors around, but we don't want anything
else to stay */
if (!theDisp) return; /* called before connection opened */
if (dirW) XDestroyWindow(theDisp, dirW);
if (infoW) XDestroyWindow(theDisp, infoW);
if (ctrlW) XDestroyWindow(theDisp, ctrlW);
if (gamW) XDestroyWindow(theDisp, gamW);
XFlush(theDisp);
}
static Cursor flcurs, fl1curs, fmcurs, fr1curs, frcurs;
/***********************************/
void LoadFishCursors()
{
#define fc_w 16
#define fc_h 16
Pixmap flpix,flmpix,fmpix,fmmpix,frpix,frmpix;
Pixmap fl1pix, fl1mpix, fr1pix, fr1mpix;
XColor fg, bg;
flcurs = fl1curs = fmcurs = fr1curs = frcurs = NULL;
flpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left_bits,
fc_w, fc_h, 1, 0, 1);
flmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_leftm_bits,
fc_w, fc_h, 1, 0, 1);
fl1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1_bits,
fc_w, fc_h, 1, 0, 1);
fl1mpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1m_bits,
fc_w, fc_h, 1, 0, 1);
fmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_mid_bits,
fc_w, fc_h, 1, 0, 1);
fmmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_midm_bits,
fc_w, fc_h, 1, 0, 1);
fr1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1_bits,
fc_w, fc_h, 1, 0, 1);
fr1mpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1m_bits,
fc_w, fc_h, 1, 0, 1);
frpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right_bits,
fc_w, fc_h, 1, 0, 1);
frmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_rightm_bits,
fc_w, fc_h, 1, 0, 1);
if (!flpix || !flmpix || !fmpix || !fmmpix || !frpix || !frmpix
|| !fl1pix || !fl1mpix || !fr1pix || !fr1mpix) return;
fg.red = fg.green = fg.blue = 0;
bg.red = bg.green = bg.blue = 0xffff;
flcurs = XCreatePixmapCursor(theDisp, flpix, flmpix, &fg, &bg, 8,8);
fl1curs= XCreatePixmapCursor(theDisp, fl1pix,fl1mpix,&fg, &bg, 8,8);
fmcurs = XCreatePixmapCursor(theDisp, fmpix, fmmpix, &fg, &bg, 8,8);
fr1curs= XCreatePixmapCursor(theDisp, fr1pix,fr1mpix,&fg, &bg, 8,8);
frcurs = XCreatePixmapCursor(theDisp, frpix, frmpix, &fg, &bg, 8,8);
if (!flcurs || !fmcurs || !frcurs || !fl1curs || !fr1curs)
{ flcurs = fmcurs = frcurs = NULL; }
}
static int fishno=0;
/***********************************/
void WaitCursor()
{
SetCursors(fishno);
fishno = (fishno+1) % 8;
}
/***********************************/
void SetCursors(n)
int n;
{
Cursor c;
/* if n < 0 sets normal cursor in all windows
n = 0..6 cycles through fish cursors */
if (n<0) {
if (!useroot && mainW) XDefineCursor(theDisp, mainW, cross);
XDefineCursor(theDisp, infoW, arrow);
XDefineCursor(theDisp, ctrlW, arrow);
XDefineCursor(theDisp, dirW, arrow);
XDefineCursor(theDisp, gamW, arrow);
fishno = 0;
}
else if (flcurs) { /* was able to load the cursors */
switch (n%8) {
case 0: c = flcurs; break;
case 1: c = fl1curs; break;
case 2: c = fmcurs; break;
case 3: c = fr1curs; break;
case 4: c = frcurs; break;
case 5: c = fr1curs; break;
case 6: c = fmcurs; break;
case 7: c = fl1curs; break;
}
if (!useroot && mainW) XDefineCursor(theDisp, mainW, c);
XDefineCursor(theDisp, infoW, c);
XDefineCursor(theDisp, ctrlW, c);
XDefineCursor(theDisp, dirW, c);
XDefineCursor(theDisp, gamW, c);
}
XFlush(theDisp);
}
/***************************************************/
static int timerdone;
/*******/
static void onalarm()
/*******/
{
timerdone=1;
}
/*******/
void Timer(n) /* waits for 'n' milliseconds */
int n;
/*******/
{
long usec;
struct itimerval it;
if (!n) return;
if (fishrunning) { sigpause(0); return; }
usec = (long) n * 1000;
memset(&it, 0, sizeof(it));
if (usec>=1000000L) { /* more than 1 second */
it.it_value.tv_sec = usec / 1000000L;
usec %= 1000000L;
}
it.it_value.tv_usec = usec;
timerdone=0;
signal(SIGALRM,onalarm);
setitimer(ITIMER_REAL, &it, (struct itimerval *)0);
while (1) {
sigblock(sigmask(SIGALRM)); /* note: have to block, so that ALRM */
if (timerdone) break; /* doesn't occur between 'if (timerdone)' */
else sigpause(0); /* and calling sigpause(0) */
}
sigblock(0); /* turn ALRM blocking off */
signal(SIGALRM,SIG_DFL);
}
\BARFOO\
else
echo "will not over write ./xvmisc.c"
fi
echo "Finished archive 2 of 8"
exit
dan
----------------------------------------------------
O'Reilly && Associates argv at sun.com / argv at ora.com
Opinions expressed reflect those of the author only.
More information about the Comp.sources.x
mailing list