home *** CD-ROM | disk | FTP | other *** search
- /*
- * iffToTiff.m, IFF to TIFF converter. Somewhat incomplete.
- * Author: Ali T. Ozer.
- * Written for 3.0, June 2, 1992.
- * Updated for 4.0 May 13, 1996.
- *
- * You may freely copy, distribute and reuse the code in this example.
- * NeXT disclaims any warranty of any kind, expressed or implied, as to its
- * fitness for any particular use.
- */
-
- #import <AppKit/AppKit.h>
-
- /* IFF related structures */
-
- typedef struct {
- char chunkID[4];
- unsigned int chunkSize;
- } ChunkHeader;
-
- typedef struct {
- short w, h, x, y;
- char nPlanes, masking, compression, pad1;
- short transparentColor;
- char xAspect, yAspect;
- short pageWidth, pageHeight;
- } BitMapHeader;
-
- typedef struct {
- BitMapHeader bmhd; /* From BMHD chunk */
- unsigned long *colorTable; /* Each entry 32 bits; 00rrggbb */
- unsigned short colorCount; /* Number of colors read in from CMAP; upto 256 */
- unsigned char *imageData; /* Image data */
- unsigned long viewMode; /* CAMG chunk */
- } ILBMInfo;
-
- /* Various IFF bits. */
-
- #define HIRES 0x8000
- #define LACE 0x0004
- #define HAM 0x0800
- #define EXTRA_HALFBRITE 0x0080
-
- #define MASKNONE 0
- #define MASKEXPLICIT 1
- #define MASKTRANSPARENTCOLOR 2
- #define MASKLASSO 3
-
- #define CMPNONE 0
- #define CMPBYTERUN1 1
-
- /* Struct for maintaining input state */
-
- typedef struct {
- const unsigned char *inputBytes;
- unsigned curLocation;
- unsigned endLocation;
- } InputBytes;
-
- /* Functions for reading input */
-
- static void badIFFError(void) {
- [NSException raise:@"BadIFFFileException" format:@"Can't read IFF file"];
- }
-
- static inline unsigned char getByte(InputBytes *input) {
- if (input->curLocation + 1 > input->endLocation) badIFFError();
- return input->inputBytes[(input->curLocation)++];
- }
-
- static inline unsigned short getShort(InputBytes *input) { /* Gets big endian short */
- unsigned short tmp;
- if (input->curLocation + 2 > input->endLocation) badIFFError();
- tmp = input->inputBytes[input->curLocation + 1] | (input->inputBytes[input->curLocation] << 8);
- input->curLocation += 2;
- return tmp;
- }
-
- static inline unsigned long getLong(InputBytes *input) {
- unsigned long tmp;
- if (input->curLocation + 4 > input->endLocation) badIFFError();
- tmp = input->inputBytes[input->curLocation + 3] | (input->inputBytes[input->curLocation + 2] << 8) | (input->inputBytes[input->curLocation + 1] << 16) | (input->inputBytes[input->curLocation] << 24);
- input->curLocation += 4;
- return tmp;
- }
-
- static inline void getBytes(InputBytes *input, unsigned char *buf, unsigned count) {
- if (input->curLocation + count > input->endLocation) badIFFError();
- memcpy(buf, input->inputBytes + input->curLocation, count);
- input->curLocation += count;
- }
-
- static void getBMHD (InputBytes *stream, BitMapHeader *bmhd) {
- bmhd->w = getShort(stream);
- bmhd->h = getShort(stream);
- bmhd->x = getShort(stream);
- bmhd->y = getShort(stream);
- bmhd->nPlanes = getByte(stream);
- bmhd->masking = getByte(stream);
- bmhd->compression = getByte(stream);
- bmhd->pad1 = getByte(stream);
- bmhd->transparentColor = getShort(stream);
- bmhd->xAspect = getByte(stream);
- bmhd->yAspect = getByte(stream);
- bmhd->pageWidth = getShort(stream);
- bmhd->pageHeight = getShort(stream);
- }
-
- static void getID (InputBytes *stream, char str[4]) {
- str[0] = getByte(stream);
- str[1] = getByte(stream);
- str[2] = getByte(stream);
- str[3] = getByte(stream);
- }
-
- static void getHeader (InputBytes *stream, ChunkHeader *header) {
- getID (stream, header->chunkID);
- header->chunkSize = getLong(stream);
- }
-
- static BOOL readILBM (InputBytes *stream, ILBMInfo *pic) {
- ChunkHeader header;
-
- memset (pic, 0, sizeof(ILBMInfo));
-
- getHeader (stream, &header);
- if (strncmp(header.chunkID, "FORM", 4)) return 0;
-
- getID (stream, header.chunkID);
- if (strncmp(header.chunkID, "ILBM", 4)) return 0;
-
- // Read chunks until we get to the body chunk
-
- while (stream->curLocation < stream->endLocation) {
-
- getHeader (stream, &header);
-
- if (strncmp(header.chunkID, "BODY", 4) == 0) {
- pic->imageData = (unsigned char *)malloc(header.chunkSize);
- getBytes (stream, pic->imageData, header.chunkSize);
- return 1; // Done, get out of here...
- } else if (strncmp(header.chunkID, "BMHD", 4) == 0) {
- getBMHD (stream, &(pic->bmhd));
- } else if (strncmp(header.chunkID, "CMAP", 4) == 0) {
- int cnt;
- BOOL oldStyleImage = YES;
- pic->colorCount = (unsigned int)(header.chunkSize/3);
- pic->colorTable = (unsigned long *)malloc(pic->colorCount * sizeof(long));
- for (cnt = 0; cnt < pic->colorCount; cnt++) {
- unsigned char r, g, b;
- r = getByte(stream);
- g = getByte(stream);
- b = getByte(stream);
- pic->colorTable[cnt] = (((unsigned long)r) << 16) | (((unsigned long)g) << 8) | (((unsigned long)b));
- }
- if (pic->colorCount & 1) stream->curLocation++; /* Align to even boundary... */
- /* Check to see if this is an old-style image */
- for (cnt = 0; cnt < pic->colorCount; cnt++) {
- if (((pic->colorTable[cnt] & 0x000f0f0f) != 0) && (pic->colorTable[cnt] != 0x00ffffff)) {
- oldStyleImage = NO;
- break;
- }
- }
- if (oldStyleImage) {
- for (cnt = 0; cnt < pic->colorCount; cnt++) {
- pic->colorTable[cnt] |= ((pic->colorTable[cnt] & 0x00f0f0f0) >> 4);
- }
- }
- } else if (strncmp(header.chunkID, "CAMG", 4) == 0) {
- pic->viewMode = getLong(stream);
- } else {
- // Ignore this chunk
- stream->curLocation += header.chunkSize + ((header.chunkSize & 1) ? 1 : 0);
- }
- }
-
- return 0;
- }
-
- static unsigned char *expandIFFBody (BitMapHeader *bmhd, unsigned char *sourceBuf) {
- signed char n;
- unsigned char *start, *cur, *destBuf;
- short lineLen, plane, i, numPlanes, rowBytes;
-
- numPlanes = (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0));
- lineLen = (bmhd->w + 7) / 8;
- destBuf = (unsigned char *)malloc(lineLen * bmhd->h * (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0)));
-
- start = sourceBuf;
- cur = destBuf;
-
- for (i = 0; i < bmhd->h; i++) {
- for (plane = 0; plane < numPlanes; plane++) { /* n planes/line */
- if (bmhd->compression == CMPBYTERUN1) { /* compressed */
- rowBytes = lineLen;
- while (rowBytes) { /* unpack until 1 scan-line complete */
- n = *sourceBuf++; /* fetch block run marker */
- if (n >= 0) {
- int move = (++n > rowBytes) ? rowBytes : n;
- memmove (cur, sourceBuf, n);
- rowBytes -= move;
- cur += move;
- sourceBuf+=n;
- } else { /* Compressed block */
- n = -n+1;
- if (n > rowBytes) {n = rowBytes;}
- rowBytes -= n;
- memset (cur, (unsigned int)*sourceBuf++, (unsigned int)n);
- cur += n;
- }
-
- }
- } else { /* uncompressed */
- memmove (cur, sourceBuf, (unsigned int)lineLen);
- sourceBuf += lineLen;
- cur += lineLen;
- }
- }
- if ((bmhd->compression == CMPNONE) && ((sourceBuf - start) & 1)){
- sourceBuf++; /* Each scanline should be in increments of 2-bytes wide */
- }
- }
-
- return destBuf;
- }
-
- NSBitmapImageRep *bitmapImageRepFromIFF(NSData *data) {
- ILBMInfo pic;
- unsigned char *tiffData, *iffData;
- unsigned char mask[8] = {128,64,32,16,8,4,2,1};
- int spp, bps, scrw, scrh, scrd, scrc, actuald;
- int readMask = 0; // Read transparency if provided?
- BOOL adjustAspectRatio = YES; // Set resolution so that the aspect ratio is correct?
- BOOL guessAspectRatio = YES; // Attempt to make a guess as to what the correct aspect ratio is?
- NSSize tiffSize = NSZeroSize;
- NSBitmapImageRep *tiff = nil;
- InputBytes stream;
-
- stream.inputBytes = [data bytes];
- stream.curLocation = 0;
- stream.endLocation = [data length];
-
- if (!readILBM (&stream, &pic)) {
- return nil;
- }
-
- scrw = pic.bmhd.w; /* Screen width in bits */
- scrh = pic.bmhd.h; /* Screen height in scanlines */
- scrd = pic.bmhd.nPlanes; /* Screen depth in bit planes */
- actuald = scrd + ((pic.bmhd.masking == MASKEXPLICIT) ? 1 : 0);
- scrc = pic.colorCount; /* Screen colors in # of color registers */
-
- /* Uncompress the IFF image */
-
- iffData = expandIFFBody (&pic.bmhd, pic.imageData);
- free (pic.imageData);
- pic.imageData = NULL;
-
- if (guessAspectRatio && adjustAspectRatio) {
- int xGuess;
- float aspect = (pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 0.0;
- if ((pic.viewMode & HIRES) && !(pic.viewMode & LACE)) {
- xGuess = 5;
- } else if (!(pic.viewMode & HIRES) && (pic.viewMode & LACE)) {
- xGuess = 20;
- } else {
- xGuess = 10;
- }
- if (fabs((((float)xGuess) / 11.0) - aspect) > 0.001) { // Might be wrong; fix it up...
- pic.bmhd.xAspect = xGuess;
- pic.bmhd.yAspect = 11;
- }
- }
-
- if (tiffSize.width < 1 || tiffSize.height < 1) {
- int realWidth, realHeight;
- float aspect = (adjustAspectRatio && pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 1.0;
- if (adjustAspectRatio) {
- realWidth = (aspect > 1.0) ? scrw : (scrw * aspect);
- realHeight = (aspect < 1.0) ? scrh : (scrh / aspect);
- } else {
- realWidth = (aspect < 1.0) ? scrw : (scrw * aspect);
- realHeight = (aspect > 1.0) ? scrh : (scrh / aspect);
- }
- if ((tiffSize.width < 1) && (tiffSize.height < 1)) {
- tiffSize.width = realWidth;
- tiffSize.height = realHeight;
- } else if (tiffSize.width < 1) {
- tiffSize.width = tiffSize.height * realWidth / realHeight;
- } else {
- tiffSize.height = tiffSize.width * realHeight / realWidth;
- }
- tiffSize.width = MAX(tiffSize.width, 1);
- tiffSize.height = MAX(tiffSize.height, 1);
- }
-
- if (scrd == 24) {
-
- unsigned char curMask;
- int rowBytes = ((scrw + 7) / 8);
- int h, w, rshift;
- unsigned char *bm, *scanline;
- register unsigned char comp;
- unsigned char r, g, b;
- register int cnt;
-
- spp = 3;
- bps = 8;
- readMask = 0;
-
- tiff = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:scrw pixelsHigh:scrh bitsPerSample:bps samplesPerPixel:spp hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
- tiffData = [tiff bitmapData];
-
- /*
-
- Video Toaster 24 Bit IFF file format:
-
- Below, rNM indicates red component, Nth pixel, Mth bit, where bits in a byte are numbered 7 6 5 4 3 2 1 0
-
- Scanline 1:
-
- byte 0 byte 1
- r00 r10 r20 r30 r40 r50 r60 r70 r80 r90 ...
-
- byte rowBytes byte rowBytes+1
- r01 r11 r21 r31 r41 r51 r61 r71 r81 r91 ...
-
- ...
-
- byte rowBytes*8
- g00 g10 g20 g30 ...
-
- byte rowBytes*8*2
-
- b00 b10 b20 b30 ...
-
- Scanline 2:
-
- byte rowBytes*8*3
-
- ...
-
- */
-
- for (h = 0; h < scrh; h++) {
- scanline = iffData + h * rowBytes * scrd;
- for (w = 0; w < scrw; w++) {
- bm = scanline + (w >> 3);
- curMask = mask[w & 7];
- rshift = 7 - (w & 7);
-
- comp = 0;
- for (cnt = 0; cnt < 8; cnt++) {
- comp += (((*bm & curMask) >> rshift) << cnt);
- bm += rowBytes;
- }
- r = comp;
-
- comp = 0;
- for (cnt = 0; cnt < 8; cnt++) {
- comp += (((*bm & curMask) >> rshift) << cnt);
- bm += rowBytes;
- }
- g = comp;
-
- comp = 0;
- for (cnt = 0; cnt < 8; cnt++) {
- comp += (((*bm & curMask) >> rshift) << cnt);
- bm += rowBytes;
- }
- b = comp;
-
- *tiffData++ = r;
- *tiffData++ = g;
- *tiffData++ = b;
- }
- }
- } else {
- unsigned char *scanLines[8], curMask;
- unsigned short alpha;
- unsigned long color = 0;
- int w, h, cnt, rshift, byte, reg;
- int rowBytes = ((scrw + 7) / 8);
- int ham, halfbrite;
-
- if (readMask && (pic.bmhd.masking == MASKEXPLICIT || pic.bmhd.masking == MASKTRANSPARENTCOLOR)) {
- readMask = pic.bmhd.masking;
- } else {
- readMask = 0;
- }
-
- halfbrite = pic.viewMode & EXTRA_HALFBRITE;
- if (ham = ((pic.viewMode & HAM) != 0)) {
- spp = 3;
- } else {
- spp = 1;
- /* is the image grayscale? (for all colors in the palette, r == g == b?) */
- for (cnt = 0; cnt < pic.colorCount; cnt++) {
- color = pic.colorTable[cnt];
- if ((((color >> 16) & 255) != (color & 255)) || (((color >> 16) & 255) != ((color >> 8) & 255))) {
- spp = 3;
- break;
- }
- }
- }
- spp += (readMask ? 1 : 0);
-
- bps = 4;
- /* can the image be represented in 4-bits? */
- for (cnt = 0; cnt < pic.colorCount; cnt++) {
- color = pic.colorTable[cnt];
- if ((pic.colorTable[cnt] & 0x00f0f0f0) != ((pic.colorTable[cnt] << 4) & 0x00f0f0f0)) {
- bps = 8;
- break;
- }
- }
-
- tiff = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
- pixelsWide:scrw
- pixelsHigh:scrh
- bitsPerSample:bps
- samplesPerPixel:spp
- hasAlpha:((readMask != 0) ? YES : NO)
- isPlanar:NO
- colorSpaceName:(spp > 2) ? NSCalibratedRGBColorSpace : NSCalibratedWhiteColorSpace
- bytesPerRow:0
- bitsPerPixel:0];
- [tiff setSize:tiffSize];
- tiffData = [tiff bitmapData];
-
- alpha = (bps == 8) ? 0x0ff : 0x0f;
-
- for (h = 0; h < scrh; h++) {
-
- for (cnt = 0; cnt < scrd; cnt++) {
- scanLines[cnt] = iffData + rowBytes * ((actuald * h) + cnt);
- }
-
- for (w = 0; w < scrw; w++) {
- curMask = mask[w & 7];
- rshift = 7 - (w & 7);
- byte = w >> 3;
- reg = 0;
- for (cnt = 0; cnt < scrd; cnt++) {
- reg += ((((*(scanLines[cnt] + byte)) & curMask) >> rshift) << cnt);
- }
- switch (readMask) {
- case MASKNONE:
- break;
- case MASKEXPLICIT:
- if ((*(iffData + rowBytes * ((actuald * h) + scrd) + byte)) & curMask) {
- alpha = (bps == 8) ? 0x0ff : 0x0f;
- } else {
- alpha = 0;
- }
- break;
- case MASKTRANSPARENTCOLOR:
- if (reg == pic.bmhd.transparentColor) {
- alpha = 0;
- } else {
- alpha = (bps == 8) ? 0x0ff : 0x0f;
- }
- break;
- }
-
- if (ham) {
- int regf = reg & 0x0f;
- if (w == 0) {
- color = pic.colorTable[0];
- }
- switch (reg & 0x030) {
- case 0x000: color = pic.colorTable[reg]; break;
- case 0x010: color = (color & 0x00ffff00) | (regf | (regf << 4)); break;
- case 0x030: color = (color & 0x00ff00ff) | ((regf | (regf << 4)) << 8); break;
- case 0x020: color = (color & 0x0000ffff) | ((regf | (regf << 4)) << 16); break;
- }
- } else if (halfbrite && (reg >= 32)) {
- color = ((pic.colorTable[reg % 32]) >> 1) & 0x007f7f7f;
- } else {
- color = pic.colorTable[reg];
- }
-
- if (!alpha) color = 0;
-
- if (bps == 8) {
- if (spp == 1 || spp == 2) {
- *tiffData++ = color & 0x0ff;
- if (spp == 2) *tiffData++ = alpha;
- } else if (spp == 3 || spp == 4) {
- *tiffData++ = (color >> 16) & 0x0ff;
- *tiffData++ = (color >> 8) & 0x0ff;
- *tiffData++ = (color & 0x0ff);
- if (spp == 4) *tiffData++ = alpha;
- }
- } else { /* bps == 4 */
- switch (spp) {
- case 1:
- if (w & 1) { /* odd pixel */
- *tiffData |= (color & 0x0f);
- tiffData++;
- } else { /* even pixel */
- *tiffData = (color & 0x0f0);
- }
- break;
- case 2:
- *tiffData++ = (color & 0x0f0) | alpha;
- break;
- case 3:
- if (w & 1) { /* odd pixel */
- *tiffData |= (color >> 16) & 0x0f; /* the red */
- tiffData++;
- *tiffData++ = ((color >> 8) & 0xf0) | (color & 0x0f); /* green & blue */
- } else { /* even pixel */
- *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
- *tiffData = (color & 0x0f0);
- }
- break;
- case 4:
- *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
- *tiffData++ = (color & 0x0f0) | alpha;
- }
- }
- }
-
- if ((spp & 1) && (bps == 4) && (scrw & 1)) tiffData++; /* We're stuck in mid byte! */
-
- }
- }
-
- free(iffData);
-
- return [tiff autorelease];
- }