home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / DEV.Z / iffToTiff.m < prev    next >
Encoding:
Text File  |  1996-08-17  |  15.3 KB  |  533 lines

  1. /*
  2.  * iffToTiff.m, IFF to TIFF converter. Somewhat incomplete.
  3.  * Author: Ali T. Ozer.
  4.  * Written for 3.0, June 2, 1992.
  5.  * Updated for 4.0 May 13, 1996.
  6.  *
  7.  * You may freely copy, distribute and reuse the code in this example.
  8.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  9.  * fitness for any particular use.
  10.  */
  11.  
  12. #import <AppKit/AppKit.h>
  13.  
  14. /* IFF related structures */
  15.  
  16. typedef struct {
  17.     char chunkID[4];
  18.     unsigned int chunkSize;
  19. } ChunkHeader;
  20.  
  21. typedef struct {
  22.     short w, h, x, y;
  23.     char  nPlanes, masking, compression, pad1;
  24.     short transparentColor;
  25.     char  xAspect, yAspect;
  26.     short pageWidth, pageHeight;
  27. } BitMapHeader;
  28.  
  29. typedef struct {
  30.     BitMapHeader    bmhd;        /* From BMHD chunk */
  31.     unsigned long   *colorTable;    /* Each entry 32 bits; 00rrggbb */
  32.     unsigned short  colorCount;        /* Number of colors read in from CMAP; upto 256 */
  33.     unsigned char   *imageData;        /* Image data */
  34.     unsigned long   viewMode;        /* CAMG chunk */
  35. } ILBMInfo;
  36.  
  37. /* Various IFF bits. */
  38.  
  39. #define    HIRES            0x8000
  40. #define    LACE            0x0004
  41. #define    HAM            0x0800
  42. #define    EXTRA_HALFBRITE        0x0080
  43.  
  44. #define MASKNONE        0
  45. #define MASKEXPLICIT        1
  46. #define MASKTRANSPARENTCOLOR    2
  47. #define MASKLASSO        3
  48.  
  49. #define CMPNONE            0
  50. #define CMPBYTERUN1        1
  51.  
  52. /* Struct for maintaining input state */
  53.  
  54. typedef struct {
  55.     const unsigned char *inputBytes;
  56.     unsigned curLocation;
  57.     unsigned endLocation;
  58. } InputBytes;
  59.  
  60. /* Functions for reading input */
  61.  
  62. static void badIFFError(void) {
  63.     [NSException raise:@"BadIFFFileException" format:@"Can't read IFF file"];
  64. }
  65.  
  66. static inline unsigned char getByte(InputBytes *input) {
  67.     if (input->curLocation + 1 > input->endLocation) badIFFError();
  68.     return input->inputBytes[(input->curLocation)++];
  69. }
  70.  
  71. static inline unsigned short getShort(InputBytes *input) {    /* Gets big endian short */
  72.     unsigned short tmp;
  73.     if (input->curLocation + 2 > input->endLocation) badIFFError();
  74.     tmp = input->inputBytes[input->curLocation + 1] | (input->inputBytes[input->curLocation] << 8);
  75.     input->curLocation += 2;
  76.     return tmp;
  77. }
  78.  
  79. static inline unsigned long getLong(InputBytes *input) {
  80.     unsigned long tmp;
  81.     if (input->curLocation + 4 > input->endLocation) badIFFError();
  82.     tmp = input->inputBytes[input->curLocation + 3] | (input->inputBytes[input->curLocation + 2] << 8) | (input->inputBytes[input->curLocation + 1] << 16) | (input->inputBytes[input->curLocation] << 24);
  83.     input->curLocation += 4;
  84.     return tmp;
  85. }
  86.  
  87. static inline void getBytes(InputBytes *input, unsigned char *buf, unsigned count) {
  88.     if (input->curLocation + count > input->endLocation) badIFFError();
  89.     memcpy(buf, input->inputBytes + input->curLocation, count);
  90.     input->curLocation += count;
  91. }
  92.  
  93. static void getBMHD (InputBytes *stream, BitMapHeader *bmhd) {
  94.     bmhd->w = getShort(stream);
  95.     bmhd->h = getShort(stream);
  96.     bmhd->x = getShort(stream);
  97.     bmhd->y = getShort(stream);
  98.     bmhd->nPlanes = getByte(stream);
  99.     bmhd->masking = getByte(stream);
  100.     bmhd->compression = getByte(stream);
  101.     bmhd->pad1 = getByte(stream);
  102.     bmhd->transparentColor = getShort(stream);
  103.     bmhd->xAspect = getByte(stream);
  104.     bmhd->yAspect = getByte(stream);
  105.     bmhd->pageWidth = getShort(stream);
  106.     bmhd->pageHeight = getShort(stream);
  107. }
  108.  
  109. static void getID (InputBytes *stream, char str[4]) {
  110.     str[0] = getByte(stream);
  111.     str[1] = getByte(stream);
  112.     str[2] = getByte(stream);
  113.     str[3] = getByte(stream);
  114. }
  115.  
  116. static void getHeader (InputBytes *stream, ChunkHeader *header) {
  117.     getID (stream, header->chunkID);
  118.     header->chunkSize = getLong(stream);
  119. }
  120.  
  121. static BOOL readILBM (InputBytes *stream, ILBMInfo *pic) {
  122.     ChunkHeader header;
  123.     
  124.     memset (pic, 0, sizeof(ILBMInfo));
  125.  
  126.     getHeader (stream, &header);
  127.     if (strncmp(header.chunkID, "FORM", 4)) return 0;
  128.     
  129.     getID (stream, header.chunkID);
  130.     if (strncmp(header.chunkID, "ILBM", 4)) return 0;
  131.     
  132.     // Read chunks until we get to the body chunk
  133.     
  134.     while (stream->curLocation < stream->endLocation) {
  135.  
  136.     getHeader (stream, &header);
  137.  
  138.     if (strncmp(header.chunkID, "BODY", 4) == 0) {
  139.         pic->imageData = (unsigned char *)malloc(header.chunkSize);
  140.         getBytes (stream, pic->imageData, header.chunkSize);
  141.         return 1;    // Done, get out of here...
  142.     } else if (strncmp(header.chunkID, "BMHD", 4) == 0) {
  143.         getBMHD (stream, &(pic->bmhd));
  144.     } else if (strncmp(header.chunkID, "CMAP", 4) == 0) {
  145.         int cnt;
  146.         BOOL oldStyleImage = YES;
  147.         pic->colorCount = (unsigned int)(header.chunkSize/3);
  148.         pic->colorTable = (unsigned long *)malloc(pic->colorCount * sizeof(long));
  149.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  150.         unsigned char r, g, b;
  151.         r = getByte(stream);
  152.         g = getByte(stream);
  153.         b = getByte(stream);
  154.         pic->colorTable[cnt] = (((unsigned long)r) << 16) | (((unsigned long)g) << 8) | (((unsigned long)b));
  155.         }
  156.         if (pic->colorCount & 1) stream->curLocation++;    /* Align to even boundary... */
  157.         /* Check to see if this is an old-style image */
  158.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  159.         if (((pic->colorTable[cnt] & 0x000f0f0f) != 0) && (pic->colorTable[cnt] != 0x00ffffff)) {
  160.             oldStyleImage = NO;
  161.             break;
  162.         }
  163.         }
  164.         if (oldStyleImage) {
  165.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  166.             pic->colorTable[cnt] |= ((pic->colorTable[cnt] & 0x00f0f0f0) >> 4);
  167.         }
  168.         }
  169.     } else if (strncmp(header.chunkID, "CAMG", 4) == 0) {
  170.         pic->viewMode = getLong(stream);
  171.     } else {
  172.         // Ignore this chunk
  173.         stream->curLocation += header.chunkSize + ((header.chunkSize & 1) ? 1 : 0);
  174.     }
  175.     }
  176.  
  177.     return 0;
  178. }      
  179.  
  180. static unsigned char *expandIFFBody (BitMapHeader *bmhd, unsigned char *sourceBuf) {
  181.     signed char n;
  182.     unsigned char *start, *cur, *destBuf;
  183.     short lineLen, plane, i, numPlanes, rowBytes;
  184.  
  185.     numPlanes = (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0));
  186.     lineLen = (bmhd->w + 7) / 8;
  187.     destBuf = (unsigned char *)malloc(lineLen * bmhd->h * (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0)));
  188.  
  189.     start = sourceBuf;
  190.     cur = destBuf;
  191.  
  192.     for (i = 0; i < bmhd->h; i++) {
  193.     for (plane = 0; plane < numPlanes; plane++) { /* n planes/line */
  194.         if (bmhd->compression == CMPBYTERUN1) { /* compressed */
  195.         rowBytes = lineLen;
  196.         while (rowBytes) { /* unpack until 1 scan-line complete */
  197.             n = *sourceBuf++; /* fetch block run marker */    
  198.             if (n >= 0) {
  199.             int move = (++n > rowBytes) ? rowBytes : n;
  200.             memmove (cur, sourceBuf, n);
  201.             rowBytes -= move;
  202.             cur += move;
  203.             sourceBuf+=n;
  204.             } else { /* Compressed block */
  205.             n = -n+1;
  206.             if (n > rowBytes) {n = rowBytes;}
  207.             rowBytes -= n;
  208.             memset (cur, (unsigned int)*sourceBuf++, (unsigned int)n);
  209.             cur += n;
  210.             }
  211.     
  212.         }
  213.         } else { /* uncompressed */
  214.         memmove (cur, sourceBuf, (unsigned int)lineLen);
  215.         sourceBuf += lineLen;
  216.         cur += lineLen;
  217.         }
  218.     }
  219.     if ((bmhd->compression == CMPNONE) && ((sourceBuf - start) & 1)){
  220.         sourceBuf++;    /* Each scanline should be in increments of 2-bytes wide */
  221.     }
  222.     }
  223.     
  224.     return destBuf;
  225. }
  226.  
  227. NSBitmapImageRep *bitmapImageRepFromIFF(NSData *data) {
  228.     ILBMInfo pic;
  229.     unsigned char *tiffData, *iffData;
  230.     unsigned char mask[8] = {128,64,32,16,8,4,2,1};
  231.     int spp, bps, scrw, scrh, scrd, scrc, actuald;
  232.     int readMask = 0;            // Read transparency if provided?
  233.     BOOL adjustAspectRatio = YES;    // Set resolution so that the aspect ratio is correct?
  234.     BOOL guessAspectRatio = YES;    // Attempt to make a guess as to what the correct aspect ratio is?
  235.     NSSize tiffSize = NSZeroSize;
  236.     NSBitmapImageRep *tiff = nil;
  237.     InputBytes stream;
  238.  
  239.     stream.inputBytes = [data bytes];
  240.     stream.curLocation = 0;
  241.     stream.endLocation = [data length];
  242.  
  243.     if (!readILBM (&stream, &pic)) {
  244.     return nil;
  245.     }
  246.  
  247.     scrw = pic.bmhd.w;         /* Screen width in bits */
  248.     scrh = pic.bmhd.h;         /* Screen height in scanlines */
  249.     scrd = pic.bmhd.nPlanes;   /* Screen depth in bit planes */
  250.     actuald = scrd + ((pic.bmhd.masking == MASKEXPLICIT) ? 1 : 0);
  251.     scrc = pic.colorCount;     /* Screen colors in # of color registers */
  252.     
  253.     /* Uncompress the IFF image */
  254.     
  255.     iffData = expandIFFBody (&pic.bmhd, pic.imageData);
  256.     free (pic.imageData);
  257.     pic.imageData = NULL;
  258.     
  259.     if (guessAspectRatio && adjustAspectRatio) {
  260.     int xGuess;
  261.     float aspect = (pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 0.0;
  262.     if ((pic.viewMode & HIRES) && !(pic.viewMode & LACE)) {
  263.         xGuess = 5;
  264.     } else if (!(pic.viewMode & HIRES) && (pic.viewMode & LACE)) {
  265.         xGuess = 20;
  266.     } else {
  267.         xGuess = 10;
  268.     }
  269.     if (fabs((((float)xGuess) / 11.0) - aspect) > 0.001) {    // Might be wrong; fix it up...
  270.         pic.bmhd.xAspect = xGuess;
  271.         pic.bmhd.yAspect = 11;
  272.     }
  273.     }
  274.  
  275.     if (tiffSize.width < 1 || tiffSize.height < 1) {
  276.     int realWidth, realHeight;
  277.     float aspect = (adjustAspectRatio && pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 1.0;
  278.     if (adjustAspectRatio) {
  279.         realWidth = (aspect > 1.0) ? scrw : (scrw * aspect);
  280.         realHeight = (aspect < 1.0) ? scrh : (scrh / aspect);
  281.     } else {
  282.         realWidth = (aspect < 1.0) ? scrw : (scrw * aspect);
  283.         realHeight = (aspect > 1.0) ? scrh : (scrh / aspect);
  284.     }
  285.     if ((tiffSize.width < 1) && (tiffSize.height < 1)) {
  286.         tiffSize.width = realWidth;
  287.         tiffSize.height = realHeight;
  288.     } else if (tiffSize.width < 1) {
  289.         tiffSize.width = tiffSize.height * realWidth / realHeight;
  290.     } else {
  291.         tiffSize.height = tiffSize.width * realHeight / realWidth;
  292.     }
  293.     tiffSize.width = MAX(tiffSize.width, 1);
  294.     tiffSize.height = MAX(tiffSize.height, 1);
  295.     }
  296.  
  297.     if (scrd == 24) {
  298.     
  299.      unsigned char curMask;
  300.     int rowBytes = ((scrw + 7) / 8);
  301.     int h, w, rshift;
  302.     unsigned char *bm, *scanline;
  303.     register unsigned char comp;
  304.     unsigned char r, g, b;
  305.     register int cnt;
  306.     
  307.     spp = 3;
  308.     bps = 8;
  309.     readMask = 0;
  310.     
  311.         tiff = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:scrw pixelsHigh:scrh bitsPerSample:bps samplesPerPixel:spp hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
  312.     tiffData = [tiff bitmapData];
  313.         
  314.     /*
  315.  
  316.     Video Toaster 24 Bit IFF file format:
  317.  
  318.     Below, rNM indicates red component, Nth pixel, Mth bit, where bits in a byte are numbered 7 6 5 4 3 2 1 0
  319.  
  320.     Scanline 1:    
  321.  
  322.         byte 0                             byte 1  
  323.         r00 r10 r20 r30 r40 r50 r60 r70    r80 r90 ...
  324.     
  325.         byte rowBytes                      byte rowBytes+1
  326.         r01 r11 r21 r31 r41 r51 r61 r71    r81 r91 ...
  327.     
  328.         ...
  329.     
  330.         byte rowBytes*8
  331.         g00 g10 g20 g30 ...
  332.  
  333.         byte rowBytes*8*2
  334.     
  335.         b00 b10 b20 b30 ...
  336.     
  337.     Scanline 2:
  338.     
  339.         byte rowBytes*8*3
  340.         
  341.         ...
  342.     
  343.     */
  344.     
  345.     for (h = 0; h < scrh; h++) {
  346.         scanline = iffData + h * rowBytes * scrd;
  347.         for (w = 0; w < scrw; w++) {
  348.         bm = scanline + (w >> 3);
  349.         curMask = mask[w & 7];
  350.         rshift = 7 - (w & 7);
  351.         
  352.         comp = 0;
  353.         for (cnt = 0; cnt < 8; cnt++) {
  354.         comp += (((*bm & curMask) >> rshift) << cnt);
  355.         bm += rowBytes;
  356.         }
  357.         r = comp;
  358.     
  359.         comp = 0;
  360.         for (cnt = 0; cnt < 8; cnt++) {
  361.         comp += (((*bm & curMask) >> rshift) << cnt);
  362.         bm += rowBytes;
  363.         }
  364.         g = comp;
  365.     
  366.         comp = 0;
  367.         for (cnt = 0; cnt < 8; cnt++) {
  368.         comp += (((*bm & curMask) >> rshift) << cnt);
  369.         bm += rowBytes;
  370.         }
  371.         b = comp;
  372.     
  373.         *tiffData++ = r;
  374.         *tiffData++ = g;
  375.         *tiffData++ = b;
  376.         }
  377.     }
  378.     } else {
  379.     unsigned char *scanLines[8], curMask;
  380.     unsigned short alpha;
  381.     unsigned long color = 0;
  382.     int w, h, cnt, rshift, byte, reg;
  383.     int rowBytes = ((scrw + 7) / 8);
  384.     int ham, halfbrite;
  385.     
  386.     if (readMask && (pic.bmhd.masking == MASKEXPLICIT || pic.bmhd.masking == MASKTRANSPARENTCOLOR)) {
  387.         readMask = pic.bmhd.masking;
  388.     } else {
  389.         readMask = 0;
  390.     }
  391.         
  392.     halfbrite = pic.viewMode & EXTRA_HALFBRITE;
  393.     if (ham = ((pic.viewMode & HAM) != 0)) {
  394.         spp = 3;
  395.     } else {
  396.         spp = 1;
  397.         /* is the image grayscale? (for all colors in the palette, r == g == b?) */
  398.         for (cnt = 0; cnt < pic.colorCount; cnt++) {
  399.         color = pic.colorTable[cnt];
  400.         if ((((color >> 16) & 255) != (color & 255)) || (((color >> 16) & 255) != ((color >> 8) & 255))) {
  401.             spp = 3;
  402.             break;
  403.         }
  404.         }
  405.     }
  406.     spp += (readMask ? 1 : 0);
  407.     
  408.     bps = 4;
  409.     /* can the image be represented in 4-bits? */
  410.     for (cnt = 0; cnt < pic.colorCount; cnt++) {
  411.         color = pic.colorTable[cnt];
  412.         if ((pic.colorTable[cnt] & 0x00f0f0f0) != ((pic.colorTable[cnt] << 4) & 0x00f0f0f0)) {
  413.         bps = 8;
  414.         break;
  415.         }
  416.     }
  417.         
  418.     tiff = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
  419.                         pixelsWide:scrw
  420.                         pixelsHigh:scrh
  421.                         bitsPerSample:bps
  422.                         samplesPerPixel:spp
  423.                         hasAlpha:((readMask != 0) ? YES : NO)
  424.                         isPlanar:NO
  425.                         colorSpaceName:(spp > 2) ? NSCalibratedRGBColorSpace : NSCalibratedWhiteColorSpace
  426.                         bytesPerRow:0
  427.                         bitsPerPixel:0];
  428.     [tiff setSize:tiffSize];
  429.     tiffData = [tiff bitmapData];
  430.     
  431.     alpha = (bps == 8) ? 0x0ff : 0x0f;
  432.         
  433.     for (h = 0; h < scrh; h++) {
  434.     
  435.         for (cnt = 0; cnt < scrd; cnt++) {
  436.         scanLines[cnt] = iffData + rowBytes * ((actuald * h) + cnt);
  437.         }
  438.         
  439.         for (w = 0; w < scrw; w++) {
  440.         curMask = mask[w & 7];
  441.         rshift = 7 - (w & 7);
  442.         byte = w >> 3;
  443.         reg = 0;
  444.         for (cnt = 0; cnt < scrd; cnt++) {
  445.             reg += ((((*(scanLines[cnt] + byte)) & curMask) >> rshift) << cnt);
  446.         }
  447.         switch (readMask) {
  448.             case MASKNONE:
  449.             break;
  450.             case MASKEXPLICIT: 
  451.             if ((*(iffData + rowBytes * ((actuald * h) + scrd) + byte)) & curMask) {
  452.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  453.             } else {
  454.                 alpha = 0;
  455.             }
  456.             break;
  457.             case MASKTRANSPARENTCOLOR: 
  458.             if (reg == pic.bmhd.transparentColor) {
  459.                 alpha = 0;
  460.             } else {
  461.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  462.             }
  463.             break;
  464.         }
  465.     
  466.         if (ham) {
  467.             int regf = reg & 0x0f;
  468.             if (w == 0) {
  469.             color = pic.colorTable[0];
  470.             }
  471.             switch (reg & 0x030) {
  472.             case 0x000: color = pic.colorTable[reg]; break;
  473.             case 0x010: color = (color & 0x00ffff00) | (regf | (regf << 4)); break;
  474.             case 0x030: color = (color & 0x00ff00ff) | ((regf | (regf << 4)) << 8); break;  
  475.             case 0x020: color = (color & 0x0000ffff) | ((regf | (regf << 4)) << 16); break;  
  476.             }
  477.         } else if (halfbrite && (reg >= 32)) {
  478.             color = ((pic.colorTable[reg % 32]) >> 1) & 0x007f7f7f;
  479.         } else {
  480.             color = pic.colorTable[reg];
  481.         }
  482.     
  483.         if (!alpha) color = 0;
  484.     
  485.         if (bps == 8) {
  486.             if (spp == 1 || spp == 2) {
  487.             *tiffData++ = color & 0x0ff;
  488.             if (spp == 2) *tiffData++ = alpha;
  489.             } else if (spp == 3 || spp == 4) {
  490.             *tiffData++ = (color >> 16) & 0x0ff;
  491.             *tiffData++ = (color >> 8) & 0x0ff;
  492.             *tiffData++ = (color & 0x0ff);
  493.             if (spp == 4) *tiffData++ = alpha;
  494.             }
  495.         } else {    /* bps == 4 */
  496.             switch (spp) {
  497.             case 1:
  498.             if (w & 1) {    /* odd pixel */
  499.                 *tiffData |= (color & 0x0f);
  500.                 tiffData++;
  501.             } else {        /* even pixel */
  502.                 *tiffData = (color & 0x0f0);
  503.             }
  504.             break;
  505.             case 2:
  506.             *tiffData++ = (color & 0x0f0) | alpha;
  507.             break;
  508.             case 3:
  509.             if (w & 1) {    /* odd pixel */
  510.                 *tiffData |= (color >> 16) & 0x0f; /* the red */
  511.                 tiffData++;
  512.                 *tiffData++ = ((color >> 8) & 0xf0) | (color & 0x0f); /* green & blue */
  513.             } else {        /* even pixel */
  514.                 *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  515.                 *tiffData = (color & 0x0f0);
  516.             }
  517.             break;
  518.             case 4:
  519.             *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  520.             *tiffData++ = (color & 0x0f0) | alpha;
  521.             }
  522.         }
  523.         }
  524.     
  525.         if ((spp & 1) && (bps == 4) && (scrw & 1)) tiffData++;    /* We're stuck in mid byte! */
  526.     
  527.     }
  528.     }
  529.  
  530.     free(iffData);
  531.  
  532.     return [tiff autorelease];
  533. }