ImageReader.st
author Claus Gittinger <cg@exept.de>
Fri, 28 Jul 2000 13:54:58 +0200
changeset 3243 26e0680294a0
parent 2646 86e3f7203efb
child 3260 e4e8e2b99900
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Object subclass:#ImageReader
	instanceVariableNames:'width height data byteOrder inStream outStream photometric
		samplesPerPixel bitsPerSample colorMap mask maskPixel
		dimensionCallBack imageSequence'
	classVariableNames:'ReverseBits'
	poolDictionaries:''
	category:'Graphics-Images-Support'
!

!ImageReader primitiveDefinitions!
%{

#ifndef _STDIO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
#endif


%}
! !

!ImageReader primitiveFunctions!
%{

/*
 * ccitt decompression
 */
static short *whiteCountTable;
static char  *whiteShiftTable;
static short *blackCountTable;
static char  *blackShiftTable;

struct ccitt_def {
    unsigned short bits;
    short nBits;
};

static struct ccitt_def 
whiteDef[] = {
    { 0x3500, 8 }, /* 0 */
    { 0x1c00, 6 },
    { 0x7000, 4 },
    { 0x8000, 4 },
    { 0xb000, 4 },
    { 0xc000, 4 },
    { 0xe000, 4 },
    { 0xf000, 4 },
    { 0x9800, 5 },
    { 0xA000, 5 },
    { 0x3800, 5 }, /* 10 */
    { 0x4000, 5 },
    { 0x2000, 6 },
    { 0x0c00, 6 },
    { 0xd000, 6 },
    { 0xd400, 6 },
    { 0xa800, 6 },
    { 0xac00, 6 },
    { 0x4e00, 7 },
    { 0x1800, 7 },
    { 0x1000, 7 }, /* 20 */
    { 0x2e00, 7 },
    { 0x0600, 7 },
    { 0x0800, 7 },
    { 0x5000, 7 },
    { 0x5600, 7 },
    { 0x2600, 7 },
    { 0x4800, 7 },
    { 0x3000, 7 },
    { 0x0200, 8 },
    { 0x0300, 8 }, /* 30 */ 
    { 0x1a00, 8 }, 
    { 0x1b00, 8 }, 
    { 0x1200, 8 }, 
    { 0x1300, 8 }, 
    { 0x1400, 8 }, 
    { 0x1500, 8 }, 
    { 0x1600, 8 }, 
    { 0x1700, 8 }, 
    { 0x2800, 8 }, 
    { 0x2900, 8 }, /* 40 */
    { 0x2a00, 8 }, 
    { 0x2b00, 8 }, 
    { 0x2c00, 8 }, 
    { 0x2d00, 8 }, 
    { 0x0400, 8 }, 
    { 0x0500, 8 }, 
    { 0x0a00, 8 }, 
    { 0x0b00, 8 }, 
    { 0x5200, 8 }, 
    { 0x5300, 8 }, /* 50 */
    { 0x5400, 8 }, 
    { 0x5500, 8 }, 
    { 0x2400, 8 }, 
    { 0x2500, 8 }, 
    { 0x5800, 8 }, 
    { 0x5900, 8 }, 
    { 0x5a00, 8 }, 
    { 0x5b00, 8 }, 
    { 0x4a00, 8 }, 
    { 0x4b00, 8 }, /* 60 */
    { 0x3200, 8 }, 
    { 0x3300, 8 }, 
    { 0x3400, 8 }, 
/* ---------------- */
    { 0xd800, 5 }, /* 64 */
    { 0x9000, 5 }, /* 128 */
    { 0x5c00, 6 }, /* 192 */
    { 0x6e00, 7 }, /* 256 */
    { 0x3600, 8 }, /* 320 */
    { 0x3700, 8 },
    { 0x6400, 8 },
    { 0x6500, 8 },
    { 0x6800, 8 },
    { 0x6700, 8 }, /* 640 */
    { 0x6600, 9 }, /* 704 */
    { 0x6680, 9 },
    { 0x6900, 9 },
    { 0x6980, 9 },
    { 0x6a00, 9 },
    { 0x6a80, 9 },
    { 0x6b00, 9 },
    { 0x6b80, 9 },
    { 0x6c00, 9 },
    { 0x6c80, 9 },
    { 0x6d00, 9 },
    { 0x6d80, 9 },
    { 0x4c00, 9 },
    { 0x4c80, 9 },
    { 0x4d00, 9 }, /* 1600 */
    { 0x6000, 6 }, /* 1664 */
    { 0x4d80, 9 }, /* 1728 */
/* -------------------------------- */
    { 0x0100, 11 }, /* 1792 */
    { 0x0180, 11 },
    { 0x01a0, 11 }, /* 1920 */
    { 0x0120, 12 }, /* 1984 */
    { 0x0130, 12 },
    { 0x0140, 12 },
    { 0x0150, 12 },
    { 0x0160, 12 },
    { 0x0170, 12 },
    { 0x01c0, 12 },
    { 0x01d0, 12 },
    { 0x01e0, 12 },
    { 0x01f0, 12 }, /* 2560 */
/* -------------------------------- */
    { 0x0010, 12 }, /* EOL */
};

static struct ccitt_def 
blackDef[] = {
    { 0x0dc0, 10 }, /* 0 */
    { 0x4000, 3 },
    { 0xc000, 2 },
    { 0x8000, 2 },
    { 0x6000, 3 },
    { 0x3000, 4 },
    { 0x2000, 4 },
    { 0x1800, 5 },
    { 0x1400, 6 },
    { 0x1000, 6 },
    { 0x0800, 7 }, /* 10 */
    { 0x0a00, 7 },
    { 0x0e00, 7 },
    { 0x0400, 8 },
    { 0x0700, 8 },
    { 0x0c00, 9 },
    { 0x05c0, 10 },
    { 0x0600, 10 },
    { 0x0200, 10 },
    { 0x0ce0, 11 },
    { 0x0d00, 11 }, /* 20 */
    { 0x0d80, 11 },
    { 0x06e0, 11 },
    { 0x0500, 11 },
    { 0x02e0, 11 },
    { 0x0300, 11 },
    { 0x0ca0, 12 },
    { 0x0cb0, 12 },
    { 0x0cc0, 12 },
    { 0x0cd0, 12 }, 
    { 0x0680, 12 }, /* 30 */
    { 0x0690, 12 }, 
    { 0x06a0, 12 }, 
    { 0x06b0, 12 }, 
    { 0x0d20, 12 }, 
    { 0x0d30, 12 }, 
    { 0x0d40, 12 }, 
    { 0x0d50, 12 }, 
    { 0x0d60, 12 }, 
    { 0x0d70, 12 },
    { 0x06c0, 12 }, /* 40 */ 
    { 0x06d0, 12 }, 
    { 0x0da0, 12 }, 
    { 0x0db0, 12 }, 
    { 0x0540, 12 }, 
    { 0x0550, 12 }, 
    { 0x0560, 12 }, 
    { 0x0570, 12 }, 
    { 0x0640, 12 }, 
    { 0x0650, 12 },
    { 0x0520, 12 }, /* 50 */ 
    { 0x0530, 12 }, 
    { 0x0240, 12 }, 
    { 0x0370, 12 }, 
    { 0x0380, 12 }, 
    { 0x0270, 12 }, 
    { 0x0280, 12 }, 
    { 0x0580, 12 }, 
    { 0x0590, 12 }, 
    { 0x02b0, 12 },
    { 0x02c0, 12 }, /* 60 */ 
    { 0x05a0, 12 }, 
    { 0x0660, 12 }, 
    { 0x0670, 12 }, 
/* ---------------- */
    { 0x03c0, 10 }, /* 64 */
    { 0x0c80, 12 }, /* 128 */
    { 0x0c90, 12 }, /* 192 */
    { 0x05b0, 12 }, /* 256 */
    { 0x0330, 12 }, /* 320 */
    { 0x0340, 12 },
    { 0x0350, 12 }, /* 448 */
    { 0x0360, 13 }, /* 512 */
    { 0x0368, 13 },
    { 0x0250, 13 }, /* 640 */
    { 0x0258, 13 }, /* 704 */
    { 0x0260, 13 },
    { 0x0268, 13 },
    { 0x0390, 13 },
    { 0x0398, 13 },
    { 0x03a0, 13 },
    { 0x03a8, 13 },
    { 0x03b0, 13 },
    { 0x03b8, 13 },
    { 0x0290, 13 },
    { 0x0298, 13 },
    { 0x02a0, 13 },
    { 0x02a8, 13 },
    { 0x02d0, 13 },
    { 0x02d8, 13 }, /* 1600 */
    { 0x0320, 13 }, /* 1664 */
    { 0x0328, 13 }, /* 1728 */
/* -------------------------------- */
};

static
initCCITTTables() {
    register cnt, bits, value;
    int nBits, index;

    if (whiteCountTable != (short *)0) return;

    whiteCountTable = (short *) malloc(sizeof(short) * 8192);
    if (! whiteCountTable) return;
    whiteShiftTable = (char *) malloc(sizeof(char) * 8192);
    if (! whiteShiftTable) {
	goto fail1;
    }
    blackCountTable = (short *) malloc(sizeof(short) * 8192);
    if (! blackCountTable) {
	goto fail2;
    }
    blackShiftTable = (char *) malloc(sizeof(char) * 8192);
    if (! blackShiftTable) {
	free(blackCountTable); blackCountTable = (short *)0;
fail2:
	free(whiteShiftTable); whiteShiftTable = (char *)0;
fail1:
	free(whiteCountTable); whiteCountTable = (short *)0;
	return;
    }

    for (index = 0; index < 8192; index++) {
	whiteCountTable[index] = -1;
	blackCountTable[index] = -1;
    }

    for (value = 0; value <= 63; value++) {
	nBits = whiteDef[value].nBits;
	bits = whiteDef[value].bits >> 3;
	for (cnt = 1 << (13 - nBits); cnt; cnt--, bits++) {
	    whiteCountTable[bits] = value;
	    whiteShiftTable[bits] = nBits;
	}
	nBits = blackDef[value].nBits;
	bits = blackDef[value].bits >> 3;
	for (cnt = 1 << (13 - nBits); cnt; cnt--, bits++) {
	    blackCountTable[bits] = value;
	    blackShiftTable[bits] = nBits;
	}
    }
    index = value;

    for (; value <= 1728; value += 64) {
	nBits = whiteDef[index].nBits;
	bits = whiteDef[index].bits >> 3;
	for (cnt = 1 << (13 - nBits); cnt; cnt--, bits++) {
	    whiteCountTable[bits] = value;
	    whiteShiftTable[bits] = nBits;
	}
	nBits = blackDef[index].nBits;
	bits = blackDef[index].bits >> 3;
	for (cnt = 1 << (13 - nBits); cnt; cnt--, bits++) {
	    blackCountTable[bits] = value;
	    blackShiftTable[bits] = nBits;
	}
	index++;
    }

    for (; value <= 2560; value += 64) {
	nBits = whiteDef[index].nBits;
	bits = whiteDef[index].bits >> 3;
	for (cnt = 1 << (13 - nBits); cnt; cnt--, bits++) {
	    whiteCountTable[bits] = value;
	    whiteShiftTable[bits] = nBits;
	    blackCountTable[bits] = value;
	    blackShiftTable[bits] = nBits;
	}
	index++;
    }
}

static short 
leftBits[] = {
     0, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF
};

static int
__decodeCCITTgroup3__(from, to, len)
    unsigned char *from;
    register unsigned char *to;
{
    register cnt;
    register short *countPtr;
    register char *shiftPtr;
    unsigned bits, bits13;
    int shift, outCount, nBitsLess13;
    int outBitOffset;
    int nLeft, t;

    if (! whiteCountTable) initCCITTTables();
    if (! whiteCountTable) return 0; /* malloc failed */

    countPtr = whiteCountTable;
    shiftPtr = whiteShiftTable;
    outCount = 0;
    outBitOffset = 0;
    bits = *from++;
    bits = (bits << 8) | *from++;
    nBitsLess13 = 3;
    for (;;) {
	bits13 = (bits >> nBitsLess13) & 0x1FFF;
	cnt = countPtr[bits13];
	if (cnt < 0) return 1;
	shift = shiftPtr[bits13];
	outCount += cnt;
	if (countPtr == blackCountTable) {
	    /* toggle if it was a terminating code */
	    if (cnt < 64) {
		countPtr = whiteCountTable;
		shiftPtr = whiteShiftTable;
	    }

	    /* draw cnt black bits */
	    if (cnt) {
		if (outBitOffset) {
		    nLeft = 8 - outBitOffset;
		    if (cnt < nLeft) nLeft = cnt;
		    t = leftBits[nLeft] >> outBitOffset;
		    *to |= t;
		    cnt -= nLeft;
		    outBitOffset += nLeft;
		    if (outBitOffset >= 8) {
			to++;
			outBitOffset -= 8;
		    }
		}
		if (cnt > 256) {
		    while ((int)to & 3) {
			*to++ = 0xFF;
			cnt -= 8;
		    }
		    while (cnt >= 32) {
			(*(long *)to) = 0xFFFFFFFF;
			to += 4;
			cnt -= 32;
		    }
		}
		while (cnt >= 8) {
		    *to++ = 0xFF;
		    cnt -= 8;
		}
		*to |= leftBits[cnt];
		outBitOffset += cnt;
	    }
	} else {
	    /* toggle if it was a terminating code */
	    if (cnt < 64) {
		countPtr = blackCountTable;
		shiftPtr = blackShiftTable;
	    }

	    /* skip cnt bits */
	    to += cnt >> 3;
	    outBitOffset += cnt & 7;
	    if (outBitOffset >= 8) {
		to++;
		outBitOffset -= 8;
	    }
	}
	if (outCount >= len) return 1;

	nBitsLess13 -= shift;
	while (nBitsLess13 < 0) {
	    bits = (bits << 8) | *from++;
	    nBitsLess13 += 8;
	}
    }
}

/*
 * LZW decompression
 */
struct buffer {
	struct buffer *prev;
	unsigned char chars[8192 - 4];
};

static int        
__decodeLZW__(from, to, inCount, fromSize, toSize)
    unsigned char *from;
    unsigned char *to;
{
    register unsigned code;
    unsigned char **strings;
    short *stringLen;
    struct buffer *scratchBuffer;
    struct buffer *newBuffer;
    unsigned char *scratchPtr;
    int nScratch;
    unsigned nextCode;
    int oldCode = -1;
    register unsigned bits;
    int nBits, mask, shift;
    int i;
    int len;
    int codeLen = 9;
    unsigned char *fromEnd, *toEnd;
    int ret = 1;        /* return success */

    fromEnd = from + fromSize;
    toEnd = to + toSize;

    scratchBuffer = (struct buffer *)malloc(sizeof(struct buffer));
    if (! scratchBuffer) return 0;

    strings = (unsigned char **)malloc(sizeof(unsigned char *) * 4096);
    if (! strings) {
	free(scratchBuffer);
	return 0;
    }
    stringLen = (short *)malloc(sizeof(short) * 4096);
    if (! stringLen) {
	free(strings);
	free(scratchBuffer);
	return 0;
    }

    scratchBuffer->prev = (struct buffer *)0;
    scratchPtr = scratchBuffer->chars;
    nScratch = sizeof(scratchBuffer->chars);

    for (i = 0; i < 256; i++) {
	*scratchPtr = i;
	strings[i] = scratchPtr++;
	stringLen[i] = 1;
    }

    nextCode = 258;
    nScratch -= 256;
    mask = 0x1FF;
    nBits = 0;
    bits = 0;
    while (inCount) {
	/* fetch code */
	while (nBits < codeLen) {
	    bits = (bits<<8) | *from++;
	    inCount--;
	    nBits += 8;
	}
	shift = nBits - codeLen;
	code = (bits >> shift) & mask;
	bits &= ~(mask << shift);
	nBits -= codeLen;
                        
	if (code == 257) break;
	if (code == 256) {
	    if (! inCount)
		break;

	    /* free stuff */
	    while (scratchBuffer->prev) {
		newBuffer = scratchBuffer;
		scratchBuffer = scratchBuffer->prev;
		free(newBuffer);
	    }
	    /* reset everything */
	    scratchPtr = scratchBuffer->chars + 256;
	    nScratch = sizeof(scratchBuffer->chars) - 256;
	    codeLen = 9;
	    nextCode = 258;
	    mask = 0x1FF;
	    /* fetch code */
	    while (nBits < codeLen) {
		bits = (bits<<8) | *from++;
		inCount--;
		nBits += 8;
	    }
	    shift = nBits - codeLen;
	    code = (bits >> shift) & mask;
	    bits &= ~(mask << shift);
	    nBits -= codeLen;
	    if (code == 257) break;
	    /* add to output */
	    if (to >= toEnd) {
		fprintf(stderr, "ImageReader [warning]: LZW outBuffer overrun\n");
		ret = 0;
		break;
	    }
	    *to++ = code;
	    oldCode = code;
	} else {
	    if (code < nextCode) {
		/* writeString(string[code]) */
		len = stringLen[code];
		bcopy(strings[code], to, len);
		to += len;

		if (oldCode != -1) {
		    /* add( string[oldcode] + first(string[code]) ) */
		    len = stringLen[oldCode] + 1;
		    if (nScratch < len) {
			newBuffer = (struct buffer *)malloc(sizeof(struct buffer));
			if (! newBuffer) goto out;
			newBuffer->prev = scratchBuffer;
			scratchBuffer = newBuffer;
			scratchPtr = scratchBuffer->chars;
			nScratch = sizeof(scratchBuffer->chars);
		    }
		    stringLen[nextCode] = len;
		    strings[nextCode] = scratchPtr;
		    bcopy(strings[oldCode], scratchPtr, len-1);
		    scratchPtr += len-1;
		    *scratchPtr++ = strings[code][0];
		    nScratch -= len;
		    nextCode++;
		}
	    } else {
		if (oldCode == -1) {
		    /* bad input */
		    ret = 0;
		    goto out;
		}
            
		/* writeString(string[oldCode] + first(string[oldCode]) ) */
		len = stringLen[oldCode];
		if ((to+len) >= toEnd) {
		    fprintf(stderr, "ImageReader [warning]: LZW outBuffer overrun\n");
		    ret = 0;
		    goto out;
		}
		bcopy(strings[oldCode], to, len);
		to += len;
		*to++ = strings[oldCode][0];

		/* add( string[oldcode] + first(string[oldCode]) ) */
		len++;
		if (nScratch < len) {
		    newBuffer = (struct buffer *)malloc(sizeof(struct buffer));
		    if (! newBuffer) goto out;
		    newBuffer->prev = scratchBuffer;
		    scratchBuffer = newBuffer;
		    scratchPtr = scratchBuffer->chars;
		    nScratch = sizeof(scratchBuffer->chars);
		}
		stringLen[nextCode] = len;
		strings[nextCode] = scratchPtr;
		bcopy(strings[oldCode], scratchPtr, len-1);
		scratchPtr += len-1;
		*scratchPtr++ = strings[oldCode][0];
		nScratch -= len;
		nextCode++;
	    }
	    oldCode = code;
	    if (nextCode >= 511)
		if (nextCode == 511) {
		    codeLen = 10;
		    mask = 0x3FF;
		} else if (nextCode >= 1023)
		    if (nextCode == 1023) {
			codeLen = 11;
			mask = 0x7FF;
		    } else 
			if (nextCode == 2047) {
			    codeLen = 12;
			    mask = 0xFFF;
			}
	}
    }
out: ;
    /* free stuff */
    while (scratchBuffer) {
	newBuffer = scratchBuffer;
	scratchBuffer = scratchBuffer->prev;
	free(newBuffer);
    }

    free(strings);
    free(stringLen);

    if (from > fromEnd) {
	fprintf(stderr, "ImageReader [warning]: LZW inBuffer overrun\n");
	ret = 0;
    }
    return ret;
}

/*
 * delta decoding (TIFF predictor = 2)
 */
static
__decodeDelta__(bytes, width, height)
    register unsigned char *bytes;
{
	register w;
	unsigned char r, g, b;

	while (height--) {
	    r = g = b = 0;
	    for (w = width; w; w--) {
		r += *bytes;
		*bytes++ = r;
		g += *bytes;
		*bytes++ = g;
		b += *bytes;
		*bytes++ = b;
	    }
	}
}

/*
 * GIF decompression
 */
static int
__decodeGIF__(from, to, inCount, initialCodeLen, fromSize, toSize)
    unsigned char *from;
    unsigned char *to;
{
    register unsigned code;
    unsigned short *prefix;
    unsigned short *suffix;
    unsigned short *outCode;
    int outCount;
    unsigned maxCode, oldCode, fin, inCode, curCode;
    register unsigned bits;
    register int nBits, mask, shift;
    int ret = 1;
    int i;
    int len;
    int endCode, clearCode, freeCode;
    int codeLen = initialCodeLen;
    unsigned char *fromEnd, *toEnd;
    static int ranges[] = {0, 1, 2, 4, 8, 16, 32, 64,
			   128, 256, 512, 1024, 2048 };

    fromEnd = from + fromSize;
    toEnd = to + toSize;

    if ((unsigned)codeLen > 12) {
	fprintf(stderr, "ImageReader [warning]: GIF bad codelen (>12)\n");
	return 0;
    }
    prefix = (unsigned short *)malloc(sizeof(short) * 4096);
    if (! prefix) return 0;
    suffix  = (unsigned short *)malloc(sizeof(short) * 4096);
    if (! suffix) {
	free(prefix);
	return 0;
    }
    outCode = (unsigned short *)malloc(sizeof(short) * 4096);
    if (! outCode) {
	free(prefix);
	free(suffix);
	return 0;
    }
    clearCode = ranges[codeLen]; /* 256 */
    endCode = clearCode + 1;     /* 257 */
    freeCode = clearCode + 2;    /* 258 */
    maxCode = clearCode << 1;    /* 512 */
    outCount = 0;

    mask = maxCode - 1;          /* 1FF */
    nBits = 0;
    bits = 0;
    while (inCount) {
	/* fetch code */
	while (nBits < codeLen) {
	    bits = bits | (*from++ << nBits);
	    inCount--;
	    nBits += 8;
	}
	if (inCount <= 0)
	    break;
	code = bits & mask;
	bits >>= codeLen;
	nBits -= codeLen;
	if (code == endCode) break;
	if (code == clearCode) {
	    if (inCount <= 0)
		break;

	    codeLen = initialCodeLen;
	    maxCode = clearCode<<1;
	    mask = maxCode - 1;
	    freeCode = clearCode + 2;  

	    /* fetch code */
	    while (nBits < codeLen) {
		bits = bits | (*from++ << nBits);
		inCount--;
		nBits += 8;
	    }
	    if (inCount <= 0)
		break;
	    code = bits & mask;
	    bits >>= codeLen;
	    nBits -= codeLen;
	    if (code == endCode) break;
	    /* add to output */
	    if (to >= toEnd) {
		fprintf(stderr, "ImageReader [warning]: GIF outBuffer overrun\n");
		ret = 0;
		break;
	    }
	    *to++ = code;
	    oldCode = fin = curCode = code;
	} else {
	    curCode = inCode = code;
	    if (curCode >= freeCode) {
		curCode = oldCode;
		outCode[outCount++] = fin;
	    }

	    while (curCode >= clearCode) {
		if (outCount > 1024) {
		    goto out;
		}

		/* debugging and protecting myself from overwrites */
		if (curCode >= 4096) {
		    fprintf(stderr, "ImageReader [warning]: GIF corrupted input (code > 4095)\n");
		    ret = 0;
		    break;
		}
		outCode[outCount++] = suffix[curCode];
		curCode = prefix[curCode];
	    }

	    fin = curCode;
	    outCode[outCount++] = fin;

#if 0
	    if ((to+outCount) >= toEnd) {
		fprintf(stderr, "ImageReader [warning]: GIF outBuffer overrun\n");
		ret = 0;
		break;
	    }
#endif
	    for (i = outCount - 1; i >= 0; i--) {
		if (to >= toEnd) {
		    fprintf(stderr, "ImageReader [warning]: GIF outBuffer overrun\n");
		    ret = 0;
		    break;
		}
		*to++ = outCode[i];
	    }
	    outCount = 0;

	    /* debugging and protecting myself from overwrites */
	    if (freeCode >= 4096) {
		fprintf(stderr, "ImageReader [warning]: GIF corrupted input (freeCode > 4095)\n");
		ret = 0;
		break;
	    }

	    prefix[freeCode] = oldCode;
	    suffix[freeCode] = fin;
	    oldCode = inCode;

	    freeCode++;
	    if (freeCode >= maxCode) {
		if (codeLen < 12) {
		    codeLen++;
		    maxCode *= 2;
		    mask = (1 << codeLen) - 1;
		}
	    }
	}
    }
out: ;
    free(prefix);
    free(suffix);
    free(outCode);

    if (from > fromEnd) {
	fprintf(stderr, "ImageReader [warning]: GIF inBuffer overrun\n");
	ret = 0;
    }
    return ret;
}

/*
 * BMP file read/decompression
 *
 * depth1 to 8bit ...
 */
static int
loadBMP1to8(w, h, fp, dest, szDest)
    int w, h;
    FILE *fp;
    unsigned char *dest;
    int szDest;
{
    int   i,j,c,bitnum,padw;
    unsigned char *pp;

    c = 0;
    padw = ((w + 31)/32) * 32;  /* 'w', padded to be a multiple of 32 */

    for (i=h-1; i>=0; i--) {
	pp = dest + (i * w);
	for (j=bitnum=0; j<padw; j++,bitnum++) {
	    if ((bitnum&7) == 0) { /* read the next byte */
		c = getc(fp);
		if (c == EOF) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: premature EOF\n"); 
		    }
		    return 0;
		}
		bitnum = 0;
	    }
	    if (j<w) {
		if (pp >= (dest+szDest)) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
		    }
		    return 0;
		}
		*pp++ = (c & 0x80) ? 1 : 0;
		c <<= 1;
	    }
	}
	if (ferror(fp)) return 0;
    }
    if (ferror(fp)) return 0;
    return 1;
}

/*
 * depth2 to 8bit ...
 */
static int
loadBMP2to8(w, h, fp, dest, szDest)
    int w, h;
    FILE *fp;
    unsigned char *dest;
    int szDest;
{
    int   i,j,c,bitnum,padw;
    unsigned char *pp;

    c = 0;
    padw = ((w + 31)/32) * 32;  /* 'w', padded to be a multiple of 32 */

    for (i=h-1; i>=0; i--) {
	pp = dest + (i * w);
	for (j=bitnum=0; j<padw; j++,bitnum++) {
	    if ((bitnum&3) == 0) { /* read the next byte */
		c = getc(fp);
		if (c == EOF) return 0;
		bitnum = 0;
	    }
	    if (j<w) {
		if (pp >= (dest+szDest)) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
		    }
		    return 0;
		}
		*pp++ = (c & 0xC0) >> 6;
		c <<= 2;
	    }
	}
	if (ferror(fp)) return 0;
    }
    if (ferror(fp)) return 0;
    return 1;
}

/*
 * depth4 to 8bit ...
 */
static int
loadBMP4to8(w, h, comp, fp, dest, szDest)
    int w, h;
    FILE *fp;
    unsigned char *dest;
    int szDest;
{
    int   i,j,c,c1,padw,x,y,nybnum;
    unsigned char *pp;

    c = c1 = 0;

    if (comp == 0) {   /* read uncompressed data */
	padw = ((w + 7)/8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */

	for (i=h-1; i>=0; i--) {
	    pp = dest + (i * w);

	    for (j=nybnum=0; j<padw; j++,nybnum++) {
		if ((nybnum & 1) == 0) { /* read next byte */
		    c = getc(fp);
		    if (c == EOF) return 0;
		    nybnum = 0;
		}

		if (j<w) {
		    if (pp >= (dest+szDest)) {
			if (@global(InfoPrinting) == true) {
			    fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
			}
			return 0;
		    }
		    *pp++ = (c & 0xf0) >> 4;
		    c <<= 4;
		}
	    }
	    if (ferror(fp)) return 0;
	}
	if (ferror(fp)) return 0;
	return 1;
    }
    if (comp == 2) {  /* read RLE4 compressed data */
	x = y = 0;  
	pp = dest + x + (h-y-1)*w;

	while (y<h) {
	    c = getc(fp); if (c == EOF) return 0;

	    if (c) {                                   /* encoded mode */
		c &= 0xFF;
		c1 = getc(fp);
		if (c1 == EOF) return 0;
		for (i=0; i<c; i++,x++,pp++) {
		    if (pp >= (dest+szDest)) {
			if (@global(InfoPrinting) == true) {
			    fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
			}
			return 0;
		    }
		    *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
		}
	    } else {    
		/* c==0x00  :  escape codes */
		c = getc(fp);  
		if (c == EOF) return 0;

		if (c == 0x00) {                    /* end of line */
		    x=0;  y++;  pp = dest + x + (h-y-1)*w;
		} else 
		    if (c == 0x01) break;               /* end of pic8 */

		    else if (c == 0x02) {                /* delta */
			c = getc(fp);  
			if (c == EOF) return 0;
			x += (c & 0xFF);
			c = getc(fp);  
			if (c == EOF) return 0;
			y += (c & 0xFF);
			pp = dest + x + (h-y-1)*w;
		    } else {                        /* absolute mode */
			c &= 0xFF;
			for (i=0; i<c; i++, x++, pp++) {
			    if ((i&1) == 0) {
				c1 = getc(fp);
				if (c1 == EOF) return 0;
			    }
			    if (pp >= (dest+szDest)) {
				if (@global(InfoPrinting) == true) {
				    fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
				}
				return 0;
			    }
			    *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
			}

			if (((c&3)==1) || ((c&3)==2)) getc(fp);  /* read pad byte */
		    }
	    }  /* escape processing */
	    if (ferror(fp)) return 0;
	}  /* while */
	if (ferror(fp)) return 0;
	return 1;
    }

    return 0;  /* bad comp */
}

/*
 * depth8 to 8bit ...
 */
static int
loadBMP8(w, h, comp, fp, dest, szDest)
    int w, h;
    FILE *fp;
    unsigned char *dest;
    int szDest;
{
    int   i,j,c,c1,padw,x,y;
    unsigned char *pp;

    if (comp == 0) {   /* uncompressed data */
	padw = ((w + 3)/4) * 4; 

	for (i=h-1; i>=0; i--) {
	    pp = dest + (i * w);

	    for (j=0; j<padw; j++) {
		c = getc(fp);  
		if (c==EOF) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: BMP premature EOF [%d]\n", __LINE__);
			fprintf(stderr, "h=%d i=%d w=%d j=%d padw=%d\n", h, i, w, j, padw);
		    }
		    return 0;
		}
		if (j<w) {
		    if (pp >= (dest+szDest)) {
			if (@global(InfoPrinting) == true) {
			    fprintf(stderr, "ImageReader [warning]: BMP outBuffer overrun\n");
			}
			return 0;
		    }
		    *pp++ = c;
		}
	    }
	    if (ferror(fp)) {
		if (@global(InfoPrinting) == true) {
		    fprintf(stderr, "ImageReader [warning]: BMP ferror set\n");
		}
		return 0;
	    }
	}
	if (ferror(fp)) {
	    if (@global(InfoPrinting) == true) {
		fprintf(stderr, "ImageReader [warning]: BMP ferror set at end\n");
	    }
	    return 0;
	}
	return 1;
    }
    if (comp == 1) {  /* RLE8 compressed */
	x = y = 0;  
	pp = dest + x + (h-y-1)*w;

	while (y<h) {
	    c = getc(fp);  
	    if (c == EOF) {
		if (@global(InfoPrinting) == true) {
		    fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
		}
		return 0;
	    }
	    if (c) {                                   /* encoded mode */
		c1 = getc(fp);
		if (c1 == EOF) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
		    }
		    return 0;
		}
		c &= 0xFF;

		for (i=0; i<c; i++,x++,pp++) {
		    if (pp >= (dest+szDest)) {
			if (@global(InfoPrinting) == true) {
			    fprintf(stderr, "ImageReader [warning]: BMP/RLE8 outBuffer overrun\n");
			}
			return 0;
		    }
		    *pp = c1;
		}
	    } else {    
		/* c==0x00  :  escape codes */
		c = getc(fp);  
		if (c == EOF) {
		    if (@global(InfoPrinting) == true) {
			fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
		    }
		    return 0;
		}
		if (c == 0x00) {                    /* end of line */
		    x=0;  y++;  pp = dest + x + (h-y-1)*w;
		} else 
		    if (c == 0x01) break;               /* end of pic8 */
		    else if (c == 0x02) {               /* delta */
			c = getc(fp);
			if (c == EOF) {
			    if (@global(InfoPrinting) == true) {
				fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
			    }
			    return 0;
			}
			x += (c & 0xFF);
			c = getc(fp);  
			if (c == EOF) {
			    if (@global(InfoPrinting) == true) {
				fprintf(stderr, "ImageReader [warning]: BMP/RLE8 premature EOF [%d]\n", __LINE__);
			    }
			    return 0;
			}
			y += (c & 0xFF);
			pp = dest + x + (h-y-1)*w;
		    } else {                            /* absolute mode */
			c &= 0xFF;
			for (i=0; i<c; i++, x++, pp++) {
			    c1 = getc(fp);
			    if (c1 == EOF) return 0;
			    if (pp >= (dest+szDest)) {
				if (@global(InfoPrinting) == true) {
				    fprintf(stderr, "ImageReader [warning]: BMP/RLE8 outBuffer overrun\n");
				}
				return 0;
			    }
			    *pp = c1;
			}

			if (c & 1) getc(fp);  /* odd length run: read an extra pad byte */
		    }
	    }  
	    if (ferror(fp)) {
		if (@global(InfoPrinting) == true) {
		    fprintf(stderr, "ImageReader [warning]: BMP/RLE8 ferror set\n");
		}
		return 0;
	    }
	} 
	if (ferror(fp)) {
	    if (@global(InfoPrinting) == true) {
		fprintf(stderr, "ImageReader [warning]: BMP/RLE8 ferror set at end\n");
	    }
	    return 0;
	}
	return 1;
    }
    if (@global(InfoPrinting) == true) {
	fprintf(stderr, "ImageReader [warning]: BMP unhandled compression: %d\n", comp);
    }
    return 0;
}

%}
! !

!ImageReader class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Abstract class to provide common functions for image-readers 
    (i.e. TIFFReader, GIFReader etc.)

    ImageReaders are created temporary to read an image from a stream.
    Normally, they are not directly used - instead, the image class is
    asked to read some file, and return an instance for it:
	Image fromFile:<someFileName>
    The Image class will guess the images format and forward the task to
    some concrete ImageReaderClass.
    If that class finds, that the files format is incorrect, other readers
    are tried until some finds the files format acceptable.

    Image readers read the stream and collect all relevant information internally.
    Once done with reading, the actual image object is created and
    data filled in from the imageReaders collected info.

    See the implementation of #fromStream: in concrete subclasses.
    The public interfaces are:
	 <ConcreteReaderClass> fromFile:aFilename
    or:
	 <ConcreteReaderClass> fromStream:aStream

    If you add a new reader, dont forget to add the method #isValidImageFile:
    which should return true, if this reader supports reading a given file.
    If your new reader class supports writing files, dont forget to add 
    #canRepresent:anImage and return true from this method.

    [See also:]
	Image Icon Form

    [author:]
	Claus Gittinger
"
! !

!ImageReader class methodsFor:'instance creation'!

new
    ^ self basicNew initialize

    "Created: 18.2.1997 / 17:08:45 / cg"
! !

!ImageReader class methodsFor:'cleanup'!

lowSpaceCleanup
    "cleanup things we do not need"

    ReverseBits := nil
! !

!ImageReader class methodsFor:'constants'!

reverseBits
    "return a table filled with bit reverse information.
     To convert from msbit-first to lsbit-first bytes, use
     the value as index into the table, retrieving the reverse
     value. Since indexing must start at 1, use (value + 1) as
     index."

    |val "{ Class: SmallInteger }" |

    ReverseBits isNil ifTrue:[
	ReverseBits := ByteArray uninitializedNew:256.
	0 to:255 do:[:i |
	    val := 0.
	    (i bitTest:16r01) ifTrue:[val := val bitOr:16r80].
	    (i bitTest:16r02) ifTrue:[val := val bitOr:16r40].
	    (i bitTest:16r04) ifTrue:[val := val bitOr:16r20].
	    (i bitTest:16r08) ifTrue:[val := val bitOr:16r10].
	    (i bitTest:16r10) ifTrue:[val := val bitOr:16r08].
	    (i bitTest:16r20) ifTrue:[val := val bitOr:16r04].
	    (i bitTest:16r40) ifTrue:[val := val bitOr:16r02].
	    (i bitTest:16r80) ifTrue:[val := val bitOr:16r01].
	    ReverseBits at:(i + 1) put:val
	]
    ].
    ^ ReverseBits
! !

!ImageReader class methodsFor:'decompression support'!

decodeDelta:step in:data width:width height:height
    "perform NeXT special predictor delta decoding inplace in data.
     Calls primitive c function for speed"

    (step ~~ 3) ifTrue:[
	^ self error:'only rgb pictures supported'
    ].

%{
    if (__isByteArray(data)
     && __bothSmallInteger(width, height)) {
	__decodeDelta__(__ByteArrayInstPtr(data)->ba_element,
		    __intVal(width), __intVal(height));
	RETURN ( self );
    }
%}
.
    self primitiveFailed
!

decompressCCITT3From:srcBytes into:dstBytes startingAt:offset count:count 
    "decompress CCITT Group 3 compressed image data.
     count bytes from srcBytes are decompressed into dstBytes.
     Calls primitive c function for speed"
%{
    if (__isByteArray(srcBytes) 
     && __isByteArray(dstBytes)
     && __bothSmallInteger(offset, count)) {
	if (__decodeCCITTgroup3__(__ByteArrayInstPtr(srcBytes)->ba_element,
				  __ByteArrayInstPtr(dstBytes)->ba_element
				  + __intVal(offset) - 1,
				  __intVal(count))) {
	    RETURN ( self );
	}
    }
%}
.
    self primitiveFailed
!

decompressGIFFrom:srcBytes count:count into:dstBytes startingAt:offset codeLen:codeLen
    "decompress GIF compressed image data.
     count bytes from srcBytes are decompressed into dstBytes.
     Calls primitive c function for speed"
%{
    if (__isByteArray(srcBytes) 
     && __isByteArray(dstBytes)
     && __bothSmallInteger(codeLen, offset)
     && __isSmallInteger(count)) {
	if (__decodeGIF__(__ByteArrayInstPtr(srcBytes)->ba_element,
			  __ByteArrayInstPtr(dstBytes)->ba_element
						+__intVal(offset) - 1,
			  __intVal(count),
			  __intVal(codeLen),
			  __byteArraySize(srcBytes),
			  __byteArraySize(dstBytes)
			 )) {
	    RETURN ( self );
	}
    }
%}
.
    self primitiveFailed
!

decompressLZWFrom:srcBytes count:count into:dstBytes startingAt:offset
    "decompress LZW (tiff) compressed image data.
     count bytes from srcBytes are decompressed into dstBytes.
     Calls primitive c function for speed"
%{
    if (__isByteArray(srcBytes) 
     && __isByteArray(dstBytes)
     && __bothSmallInteger(offset, count)) {
	if (__decodeLZW__(__ByteArrayInstPtr(srcBytes)->ba_element,
			  __ByteArrayInstPtr(dstBytes)->ba_element
					      + __intVal(offset) - 1,
			  __intVal(count),
			  __byteArraySize(srcBytes),
			  __byteArraySize(dstBytes)
			)) {
	    RETURN ( self );
	}
    }
%}
.
    self primitiveFailed
!

decompressRLEFrom:srcBytes at:srcStartIndex into:dstBytes at:dstStartIndex increment:dstIncrement
    "common helper to expand RLE encoded data"

    "/ the code below expands common RLE formats, where the high bit
    "/ controls how the count in the low 7 bits is to be interpreted:
    "/ 1 means: count verbatim bytes follow;
    "/ 0 means: repeat the next byte count times.
    "/ Decoding stops when a 0-count is encountered.
    "/ srcBytes and dstBytes should not overlap.
    "/ dstIncrement controls the stepping in the destination (usually: 1)
    "/
    "/ this may be recoded in C in the future

    |srcIdx "{Class: SmallInteger }"
     dstIdx "{Class: SmallInteger }"
     pixel  "{Class: SmallInteger }"
     count  "{Class: SmallInteger }"|

    srcIdx := srcStartIndex.
    dstIdx := dstStartIndex.
    [true] whileTrue:[
	pixel := srcBytes at:srcIdx.
	count := pixel bitAnd:16r7F.
	count == 0 ifTrue:[
	    ^ self
	].
	srcIdx := srcIdx + 1.
	(pixel bitAnd:16r80) == 0 ifTrue:[
	    "/ run bytes
	    pixel := srcBytes at:srcIdx.
	    srcIdx := srcIdx + 1.
	    dstIncrement == 1 ifTrue:[
		dstBytes from:dstIdx to:(dstIdx+count-1) put:pixel.
		dstIdx := dstIdx + count.
	    ] ifFalse:[
		1 to:count do:[:c |
		    dstBytes at:dstIdx put:pixel.
		    dstIdx := dstIdx + dstIncrement.
		]
	    ]
	] ifFalse:[
	    "/ verbatim bytes
	    dstIncrement == 1 ifTrue:[
		dstBytes replaceFrom:dstIdx to:(dstIdx+count-1) with:srcBytes startingAt:srcIdx.
		srcIdx := srcIdx + count.
		dstIdx := dstIdx + count.
	    ] ifFalse:[
		1 to:count do:[:c |
		    dstBytes at:dstIdx put:(srcBytes at:srcIdx).
		    srcIdx := srcIdx + 1.
		    dstIdx := dstIdx + dstIncrement.
		]
	    ]
	]
    ].

    "Modified: 23.4.1997 / 18:54:05 / cg"
!

loadBMP1to8Width:width height:height from:aStream into:aByteArray
    "load bmp-1 bit per pixel imagedata. A helper for BMP image reader.
     Calls primitive c function for speed"

    |f|

    aStream isExternalStream ifFalse:[^ false].
    f := aStream filePointer.
    f isNil ifTrue:[^ false].
%{
    if (! (__bothSmallInteger(width, height)
	   && __isByteArray(aByteArray))) {
	RETURN (false);
    }
    if (loadBMP1to8(__intVal(width), __intVal(height), 
		 __FILEVal(f), 
		__ByteArrayInstPtr(aByteArray)->ba_element,
		__byteArraySize(aByteArray) )) {
	RETURN (true);
    }
    RETURN (false);
%}

    "Modified: 22.4.1996 / 19:14:32 / cg"
!

loadBMP2to8Width:width height:height from:aStream into:aByteArray
    "load bmp-2 bit per pixel imagedata. A helper for BMP image reader.
     Calls primitive c function for speed"

    |f|

    aStream isExternalStream ifFalse:[^ false].
    f := aStream filePointer.
    f isNil ifTrue:[^ false].
%{
    if (! (__bothSmallInteger(width, height)
	   && __isByteArray(aByteArray))) {
	RETURN (false);
    }
    if (loadBMP2to8(__intVal(width), __intVal(height), 
	     __FILEVal(f), 
	     __ByteArrayInstPtr(aByteArray)->ba_element,
	     __byteArraySize(aByteArray) )) {
	RETURN (true);
    }
    RETURN (false);
%}

    "Modified: 22.4.1996 / 19:14:40 / cg"
!

loadBMP4to8Width:width height:height compression:compression from:aStream into:aByteArray
    "load bmp-4 bit per pixel imagedata. A helper for BMP image reader.
     Calls primitive c function for speed"

    |f|

    aStream isExternalStream ifFalse:[^ false].
    f := aStream filePointer.
    f isNil ifTrue:[^ false].
%{
    if (! (__bothSmallInteger(width, height)
	   && __isSmallInteger(compression)
	   && __isByteArray(aByteArray))) {
	RETURN (false);
    }
    if (loadBMP4to8(__intVal(width), __intVal(height), __intVal(compression), 
	     __FILEVal(f), 
	     __ByteArrayInstPtr(aByteArray)->ba_element,
	     __byteArraySize(aByteArray) )) {
	RETURN (true);
    }
    RETURN (false);
%}

    "Modified: 22.4.1996 / 19:14:46 / cg"
!

loadBMP8Width:width height:height compression:compression from:aStream into:aByteArray
    "load bmp-8 bit per pixel imagedata. A helper for BMP image reader.
     Calls primitive c function for speed"

|f|

    aStream isExternalStream ifFalse:[^ false].
    f := aStream filePointer.
    f isNil ifTrue:[^ false].
%{
    if (! (__bothSmallInteger(width, height)
	   && __isSmallInteger(compression)
	   && __isByteArray(aByteArray))) {
	RETURN (false);
    }
    if (loadBMP8(__intVal(width), __intVal(height), __intVal(compression), 
	     __FILEVal(f), 
	     __ByteArrayInstPtr(aByteArray)->ba_element,
	     __byteArraySize(aByteArray) )) {
	RETURN (true);
    }
    RETURN (false);
%}

    "Modified: 22.4.1996 / 19:14:54 / cg"
! !

!ImageReader class methodsFor:'i/o support'!

streamReadingFile:aFilename
    "return a stream to read aFilename.
     If the filename ends with '.Z' or '.gz', return a stream
     to a pipe for the uncompressor. Otherwise, return a stream to read
     the file directly."

    |inStream name fn|

    name := aFilename asString.
    ((name endsWith:'.Z') or:[name endsWith:'.gz']) ifTrue:[
	fn := Smalltalk getSystemFileName:name.
	inStream := PipeStream readingFrom:'gunzip < ' , fn.
    ] ifFalse:[
	inStream := Smalltalk systemFileStreamFor:aFilename.
	inStream isNil ifTrue:[
	    inStream := Smalltalk bitmapFileStreamFor:aFilename
	]
    ].
    inStream isNil ifTrue:[
	'ImageReader [warning]: open error on: ' infoPrint. aFilename infoPrintCR. 
    ].
    ^ inStream

    "Modified: 31.1.1997 / 10:37:24 / cg"
! !

!ImageReader class methodsFor:'image reading'!

fromFile:aFileName
    "read an image (in my format) from aFileName. 
     Return the image or nil on error."

    |reader|

    reader := self readFile:aFileName.
    reader notNil ifTrue:[
	^ reader image
    ].
    ^ nil

    "
     XBMReader fromFile:'bitmaps/SBrowser.xbm'
     XPMReader fromFile:'bitmaps/xpmBitmaps/BOOK.xpm'
     XBMReader fromFile:'bitmaps/xpmBitmaps/BOOK.xpm'
    "

    "Modified: 4.4.1997 / 22:09:49 / cg"
!

fromStream:aStream
    "read an image (in my format) from aStream.
     Return the image or nil (if unrecognized format or error).
     The stream remains open."

    |reader|

    reader := self new fromStream:aStream.
    reader notNil ifTrue:[
	^ reader image
    ].
    ^ nil

    "Modified: 4.4.1997 / 22:11:49 / cg"
!

imagesFromFile:aFileName
    "read all images (in my format) from aFileName. 
     Return a collection of images or nil on error.
     Not all reader may support multiple images."

    |reader|

    reader := self readFile:aFileName.
    reader notNil ifTrue:[
	^ reader images
    ].
    ^ nil

    "Modified: 4.4.1997 / 22:11:01 / cg"
!

imagesFromStream:aStream
    "read all images (in my format) from aStream.
     Return a collection of images or nil (if unrecognized format or error).
     The stream remains open.
     Not all reader may support multiple images."

    |reader|

    reader := self new fromStream:aStream.
    reader notNil ifTrue:[
	^ reader images
    ].
    ^ nil

    "Modified: 4.4.1997 / 22:11:40 / cg"
!

readFile:aFileName
    "create a reader and let it read an image (in my format) from aFileName. 
     Return the reader."

    |reader inStream|

    inStream := self streamReadingFile:aFileName.
    inStream isNil ifTrue:[
	'ImageReader [warning]: file open error' errorPrintCR.
	^ nil
    ].
    reader := self new fromStream:inStream.
    inStream close.
    ^ reader

    "
     XPMReader readFile:'bitmaps/xpmBitmaps/BOOK.xpm'
    "

    "Modified: 4.4.1997 / 22:06:49 / cg"
!

readStream:inStream
    "create a reader and let it read a stream (in my format). 
     Return the reader. The stream remains open."

    ^ self new fromStream:inStream.

    "Created: 4.4.1997 / 22:08:13 / cg"
! !

!ImageReader class methodsFor:'image support'!

buildMaskFromColor:maskPixelValue for:pixels width:width height:height
    "helper for image formats, where an individual pixel value
     has been defined as a mask-pixel (i.e. GIF).
     Creates a maskImage, with zeros at positions where the image
     has the given pixelValue; all other mask pixels are set to 1."

    |maskArray bytesPerMaskRow mask|

    bytesPerMaskRow := (width+7) // 8.

    maskArray := ByteArray uninitializedNew:bytesPerMaskRow * height.

%{
    int __w = __intVal(width);
    int __h = __intVal(height);
    int __x, __y;
    int __outBits, __nOut;
    unsigned char *__inP, *__outP, *__nextOutRow;
    int __bpr = __intVal(bytesPerMaskRow);
    int __maskPixel = __intVal(maskPixelValue);
#ifdef DEBUG
    unsigned char *outEnd;
#endif

    if (! __isByteArray(pixels)) goto fail;
    if (! __isByteArray(maskArray)) goto fail;

    __inP = __ByteArrayInstPtr(pixels)->ba_element;
    __outP = __ByteArrayInstPtr(maskArray)->ba_element;

#ifdef DEBUG
    outEnd = __outP + (__byteArraySize(maskArray));
#endif
/*
printf("outP: %x outEnd: %x sz: %d\n", __outP, outEnd, __byteArraySize(maskArray));
*/

    for (__y=__h; __y>0; __y--) {
	__outBits = 0;
	__nOut = 8;
	__nextOutRow = __outP + __bpr;

	for (__x=__w; __x>=8; __x-=8) {
	    if (__inP[0] != __maskPixel) { __outBits |= 0x80; };
	    if (__inP[1] != __maskPixel) { __outBits |= 0x40; };
	    if (__inP[2] != __maskPixel) { __outBits |= 0x20; };
	    if (__inP[3] != __maskPixel) { __outBits |= 0x10; };
	    if (__inP[4] != __maskPixel) { __outBits |= 0x08; };
	    if (__inP[5] != __maskPixel) { __outBits |= 0x04; };
	    if (__inP[6] != __maskPixel) { __outBits |= 0x02; };
	    if (__inP[7] != __maskPixel) { __outBits |= 0x01; };
	    __inP += 8;
	    *__outP++ = __outBits;
	    __outBits = 0;
	}
	for (; __x>0; __x--) {
	    __outBits <<= 1;
	    if (*__inP != __maskPixel) {
		__outBits |= 1;
	    }
	    __inP++;

/*
 printf("x: %d  bits: %x\n", __x, __outBits);
*/
	    if (--__nOut == 0) {
#ifdef DEBUG
		if (__outP >= outEnd) {
		 printf("oops\n");
		 goto fail;
		}
#endif
		*__outP = __outBits;

		__outP++;
		__nOut = 8;
		__outBits = 0;
	    }
	}

	if (__nOut != 8) {
	    __outBits <<= __nOut;

#ifdef DEBUG
	    if (__outP >= outEnd) {
	     printf("oops2\n");
	     goto fail;
	    }
#endif
	    *__outP = __outBits;

	}
	__outP = __nextOutRow;
    }
fail: ;
%}.
    mask := ImageMask width:width height:height fromArray:maskArray.
    ^ mask

    "Created: 21.6.1996 / 11:43:47 / cg"
! !

!ImageReader class methodsFor:'image writing'!

save:anImage onFile:aFileName
    "save the image in my format on aFileName"

    ^ (self basicNew) save:anImage onFile:aFileName

    "Modified: 10.4.1997 / 17:42:57 / cg"
! !

!ImageReader class methodsFor:'testing'!

canRepresent:anImage
    "return true, if anImage can be represented in my file format.
     must be redefined in concrete subclasses which support saving."

    ^ false
!

isValidImageFile:aFileName
    "return true, if aFileName contains an image this
     reader understands - must be redefined in concrete subclasses
     which support reading."

    ^ false
! !

!ImageReader methodsFor:'accessing'!

bitsPerPixel
    "return the number of bits per pixel"

    ^ (bitsPerSample inject:0 into:[:sum :i | sum + i])
!

bitsPerRow
    "return the number of bits in one scanline of the image"

    ^  width * (self bitsPerPixel).

    "Created: 31.1.1997 / 10:38:42 / cg"
!

bitsPerSample
    "return the number of bits per sample"

    ^ bitsPerSample

    "Modified: 22.4.1996 / 19:15:18 / cg"
!

bytesPerRow
    "return the number of bytes in one scanline of the image"

    |bitsPerRow bytesPerRow|

    bitsPerRow := width * (self bitsPerPixel).
    bytesPerRow := bitsPerRow // 8.
    ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
	bytesPerRow := bytesPerRow + 1
    ].
    ^ bytesPerRow

    "Created: 31.1.1997 / 10:39:06 / cg"
!

colorMap
    "return the colormap"

    ^ colorMap

    "Modified: 22.4.1996 / 19:15:24 / cg"
!

data 
    "return the raw image data"

    ^ data

    "Modified: 22.4.1996 / 19:15:31 / cg"
!

dimensionCallBack:aBlock
    "set the block, which is evaluated during the readProcess, as soon as
     the images dimension is known. This is useful for background image reading,
     if the size is need to be known (for example: for formatting purposes)"

    dimensionCallBack := aBlock

    "Created: 14.9.1996 / 17:10:58 / cg"
!

hasMultipleImages
    ^ imageSequence size > 1

    "Created: 4.4.1997 / 21:37:06 / cg"
    "Modified: 24.6.1997 / 15:33:43 / cg"
!

height 
    "return the height of the image"

    ^ height

    "Modified: 22.4.1996 / 19:15:39 / cg"
!

image
    "return the image as represented by myself;
     If my file contained multiple images, return the first one."

    imageSequence size > 0 ifTrue:[
	^ self images first.
    ].

    ^ self makeImage.

    "Modified: / 15.1.1998 / 15:46:24 / stefan"
    "Modified: / 1.4.1998 / 14:31:35 / cg"
!

imageFrames
    "return a collection of all imageFrames as represented by myself.
     ImageFrames are wrappers for individual images, which hold
     additional information (such as image delay time).
     Nil is return for single image formats/files."

    ^ imageSequence

    "Created: / 1.4.1998 / 14:37:42 / cg"
!

images
    "return a collection of all images as represented by myself.
     For compatibility with single-image formats, return a collection
     containing my single image here if the reader does not support
     multiple images.
     Readers for formats with multiple images should leave the images
     in the imageSequence instVar as a side effect of reading."

    |img|

    imageSequence notNil ifTrue:[
	^ (imageSequence collect:[:aFrame | aFrame image])
    ].

    img := self makeImage.
    img isNil ifTrue:[
	^ img
    ].
    ^ Array with:img

    "Modified: / 1.4.1998 / 14:36:23 / cg"
!

makeImage
    "return the image as represented by the values found in my
     instvars; these have been typically set as a side effect of
     the fromStream:-method, which reads images."

    |image depth|

    depth := self bitsPerPixel.
    image := (Image implementorForDepth:depth) new.
    image depth:depth.
    inStream isFileStream ifTrue:[
	image fileName:inStream pathName.
    ].
    image 
	width:width 
	height:height
	photometric:photometric
	samplesPerPixel:samplesPerPixel
	bitsPerSample:bitsPerSample
	colorMap:colorMap
	bits:data
	mask:mask.

    imageSequence notNil ifTrue:[
	image imageSequence:imageSequence.
    ].

    ^ image

    "Modified: / 15.1.1998 / 15:46:24 / stefan"
    "Created: / 1.4.1998 / 14:09:45 / cg"
    "Modified: / 1.4.1998 / 14:41:35 / cg"
!

mask
    "return the image mask (or nil)"

    ^ mask

    "Modified: 22.4.1996 / 19:15:31 / cg"
    "Created: 20.6.1996 / 17:08:29 / cg"
!

numberOfImages
    "return the number of images as read.
     By default (here), I hold a single image;
     however, some fileFormats allow for multiple images"

    |n|

    (n := imageSequence size) > 0 ifTrue:[
	^ n
    ].
    ^ 1

    "Created: 21.6.1997 / 18:33:45 / cg"
    "Modified: 24.6.1997 / 15:33:26 / cg"
!

photometric
    "return the photometric interpretation of the image data"

    ^ photometric

    "Modified: 22.4.1996 / 19:15:57 / cg"
!

samplesPerPixel
    "return the number of samples per pixel"

    ^ samplesPerPixel

    "Modified: 22.4.1996 / 19:16:03 / cg"
!

width
    "return the width of the image"

    ^ width

    "Modified: 22.4.1996 / 19:16:10 / cg"
! !

!ImageReader methodsFor:'accessing - private'!

byteOrder:aSymbol
    "set the byte order - either #lsb or #msb"

    byteOrder := aSymbol

    "Created: 15.2.1997 / 13:58:50 / cg"
!

inStream
    ^ inStream

    "Created: 15.2.1997 / 13:57:40 / cg"
!

inStream:aStream
    inStream := aStream

    "Created: 15.2.1997 / 13:57:35 / cg"
! !

!ImageReader methodsFor:'error reporting'!

fileFormatError:aMessage
    "report a format error - no image could be read"

    Image 
	badImageFormatQuerySignal
	    raiseErrorString:(self class name , ' [info]: ' , aMessage).
    ^ nil

    "Created: / 3.2.1998 / 17:50:06 / cg"
! !

!ImageReader methodsFor:'i/o support'!

readLong
    "return the next 4-byte long, honoring the byte-order"

    ^ inStream nextLongMSB:(byteOrder ~~ #lsb)
!

readShort
    "return the next 2-byte short, honoring the byte-order"

    ^ inStream nextUnsignedShortMSB:(byteOrder ~~ #lsb)
!

readShortLong
    "return the next 2-byte short, honoring the byte-order.
     There are actually 4 bytes read, but only 2 looked at."

    |bytes val|

    bytes := ByteArray uninitializedNew:4.
    inStream nextBytes:4 into:bytes.
    (byteOrder == #lsb) ifTrue:[
	val := bytes at:2.
	val := val * 256 + (bytes at:1)
    ] ifFalse:[
	val := bytes at:3.
	val := val * 256 + (bytes at:4)
    ].
    ^ val
!

writeLong:anInteger
    "write a 4-byte long, honoring the byte-order."

    outStream nextPutLong:anInteger MSB:(byteOrder ~~ #lsb)
!

writeShort:anInteger
    "write a 2-byte short, honoring the byte-order."

    outStream nextPutShort:anInteger MSB:(byteOrder ~~ #lsb)
! !

!ImageReader methodsFor:'image reading'!

fromStream:aStream
    "read an image in my format from aStream.
     Leave image description in instance variables."


    ^ self subclassResponsibility

    "Modified: 22.4.1996 / 19:11:31 / cg"
! !

!ImageReader methodsFor:'image reading support'!

buildMaskFromColor:maskPixelValue
    "helper for image formats, where an individual pixel value
     has been defined as a mask-pixel (i.e. GIF).
     Creates a maskImage, with zeros at positions where the image
     has the given pixelValue; all other mask pixels are set to 1."

    mask := self class 
		buildMaskFromColor:maskPixelValue 
		for:data
		width:width
		height:height

    "Modified: / 14.1.1998 / 14:48:33 / cg"
! !

!ImageReader methodsFor:'image writing'!

save:image onFile:aFileName
    "save image in my format on aFile"

    ^ Image cannotRepresentImageSignal 
	raiseWith:image
	errorString:('image save not implemented for this format').

    "Modified: 10.4.1997 / 17:43:52 / cg"
! !

!ImageReader methodsFor:'initialization'!

initialize

    "Created: 18.2.1997 / 17:08:31 / cg"
! !

!ImageReader class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libview/ImageReader.st,v 1.70 2000-07-28 11:54:58 cg Exp $'
! !