home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / ImageFilter / iffToTiff.m next >
Encoding:
Text File  |  1992-07-24  |  12.4 KB  |  435 lines

  1. /*
  2.  * iffToTiff.m, IFF to TIFF converter. Somewhat incomplete.
  3.  * Author: Ali T. Ozer, NeXT Computer, Inc.
  4.  * Written for 3.0, June 2, 1992.
  5.  *
  6.  * You may freely copy, distribute and reuse the code in this example.
  7.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  8.  * fitness for any particular use.
  9.  */
  10.  
  11. #import <appkit/appkit.h>
  12. #import <libc.h>
  13.  
  14. /* IFF related structures */
  15.  
  16. typedef struct {
  17.     char chunkID[4];
  18.     unsigned int #kSize;
  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. static inline unsigned char getByte (NXStream *stream)
  53. {
  54.     return (unsigned char)NXGetc(stream);    
  55. }
  56.  
  57. static void getBytes (NXStream *stream, unsigned char *buf, int count)
  58. {
  59.     NXRead(stream, buf, count);    
  60. }
  61.  
  62. static inline unsigned short getShort (NXStream *stream)
  63. {
  64.     return (((unsigned short)getByte(stream)) << 8) + getByte(stream);
  65. }
  66.  
  67. static inline unsigned long getLong (NXStream *stream)
  68. {
  69.     return (((unsigned long)getShort(stream)) << 16) + getShort(stream);
  70. }
  71.  
  72. static void getBMHD (NXStream *stream, BitMapHeader *bmhd)
  73. {
  74.     bmhd->w = getShort(stream);
  75.     bmhd->h = getShort(stream);
  76.     bmhd->x = getShort(stream);
  77.     bmhd->y = getShort(stream);
  78.     bmhd->nPlanes = getByte(stream);
  79.     bmhd->masking = getByte(stream);
  80.     bmhd->compression = getByte(stream);
  81.     bmhd->pad1 = getByte(stream);
  82.     bmhd->transparentColor = getShort(stream);
  83.     bmhd->xAspect = getByte(stream);
  84.     bmhd->yAspect = getByte(stream);
  85.     bmhd->pageWidth = getShort(stream);
  86.     bmhd->pageHeight = getShort(stream);
  87. }
  88.  
  89. static void getID (NXStream *stream, char str[4])
  90. {
  91.     str[0] = getByte(stream);
  92.     str[1] = getByte(stream);
  93.     str[2] = getByte(stream);
  94.     str[3] = getByte(stream);
  95. }
  96.  
  97. static void getHeader (NXStream *stream, ChunkHeader *header)
  98. {
  99.     getID (stream, header->chunkID);
  100.     header->chunkSize = getLong(stream);
  101. }
  102.  
  103. static BOOL readILBM (NXStream *stream, ILBMInfo *pic)
  104. {
  105.     ChunkHeader header;
  106.     
  107.     bzero (pic, sizeof(ILBMInfo));
  108.  
  109.     getHeader (stream, &header);
  110.     if (strncmp(header.chunkID, "FORM$)) return 0;
  111.     
  112.     getID (stream, header.chunkID);
  113.     if (strncmp(header.chunkID, "ILBM", 4)) return 0;
  114.     
  115.     // Read chunks until we get to the body chunk
  116.     
  117.     while (!NXAtEOS(stream)) {
  118.  
  119.     getHeader (stream, &header);
  120.  
  121.     if (strncmp(header.chunkID, "BODY", 4) == 0) {
  122.         pic->imageData = (unsigned char *)malloc(header.chunkSize);
  123.         getBytes (stream, pic->imageData, header.chunkSize);
  124.         return 1;    // Done, get out of here...
  125.     } else if (strncmp(header.chunkID, "BMHD", 4) == 0) {
  126.         getBMHD (stream, &(pic->bmhd));
  127.     } else if (strncmp(header.chunkID, "CMAP", 4) == 0) {
  128.         int cnt;
  129.         BOOL oldStyleImage = YES;
  130.         pic->colorCount = (unsigned int)(header.chunkSize/3);
  131.         pic->colorTable = (unsigned long *)malloc(pic->colorCount * sizeof(long));
  132.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  133.         unsigned char r, g, b;
  134.         r = getByte(stream);
  135.         g = getByte(stream);
  136.         b = getByte(stream);
  137.         pic->colorTable[cnt] = (((unsigned long)r) << 16) | (((unsigned long)g) << 8) | (((unsigned long)b));
  138.         }
  139.         if (pic->colorCount & 1) NXSeek (stream, 1, NX_FROMCURRENT);    /* Align... */
  140.         /* Check to see if this is an old-style image */
  141.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  142.         if (((pic->colorTable[cnt] & 0x000f0f0f) != 0) && (pic->colorTable[cnt] != 0x00ffffff)) {
  143.             oldStyleImage = NO;
  144.             break;
  145.         }
  146.         }
  147.         if (oldStyleImage) {
  148.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  149.             pic->colorTable[cnt] |= ((pic->colorTable[cnt] & 0x00f0f0f0) >> 4);
  150.         }
  151.         }
  152.     } else if (strncmp(header.chunkID, "CAMG", 4) == 0) {
  153.         pic->viewMode = getLong(stream);
  154.     } else {
  155.         // Ignore this chunk
  156.         NXSeek (stream, header.chunkSize + ((header.chunkSize & 1) ? 1 : 0), NX_FROMCURRENT);
  157.     }
  158.     }
  159.  
  160.     return 0;
  161. }      
  162.  
  163. static unsigned char *expandIFFBody (BitMapHeader  *bmhd, unsigned char *sourceBuf)
  164. {
  165.     signed char n;
  166.     unsigned char *start, *cur, *destBuf;
  167.     short lineLen, plane, i, numPlanes, rowBytes;
  168.  
  169.     numPlanes = (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0));
  170.     lineLen = (bmhd->w + 7) / 8;
  171.     destBuf = (unsigned char *)malloc(lineLen * bmhd->h * (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0)));
  172.  
  173.     start = sourceBuf;
  174.     cur = destBuf;
  175.  
  176.     for (i = 0; i < bmhd->h; i++) {
  177.     for (plane = 0; plane < numPlanes; plane++) { /* n planes/line */
  178.     %if (bmhd->compression == CMPBYTERUN1) { /* compressed */
  179.         rowBytes = lineLen;
  180.         while (rowBytes) { /* unpack until 1 scan-line complete */
  181.             n = *sourceBuf++; /* fetch block run marker */    
  182.             if (n >= 0) {
  183.             int move = (++n > rowBytes) ? rowBytes : n;
  184.             memmove (cur, sourceBuf, n);
  185.             rowBytes -= move;
  186.             cur += move;
  187.             sourceBuf+=n;
  188.             } else { /* Compressed block */
  189.             n = -n+1;
  190.             if (n > rowBytes) {n = rowBytes;}
  191.             rowBytes -= n;
  192.             memset (cur, (unsigned int)*sourceBuf++, (unsigned int)n);
  193.             cur += n;
  194.             }
  195.     
  196.         }
  197.         } else { /* uncompressed */
  198.         memmove (cur, sourceBuf, (unsigned int)lineLen);
  199.         sourceBuf += lineLen;
  200.         cur += lineLen;
  201.         }
  202.     }
  203.     if ((bmhd->compression == CMPNONE) && ((sourceBuf - start) & 1)){
  204.         sourceBuf++;    /* Each scanline should be in increments of 2-bytes wide */
  205.     }
  206.     }
  207.     
  208.     return destBuf;
  209. }
  210.  
  211. NXBitmapImageRep *convertIFFToTIFF (NXStream *stream)
  212. {
  213.     ILBMInfo pic;
  214.     unsigned char *tiffData, *iffData;
  215.     unsigned char mask[8] = {128,64,32,16,8,4,2,1};
  216.     int spp, bps, scrw, scrh, scrd, scrc, actuald, cnt;
  217.     int readMask = 0;            // Read transparency if provided?
  218.     BOOL adjustAspectRatio = YES;    // Set resolution so that the aspect ratio is correct?
  219.     BOOL guessAspectRatio = YES;    // Attempt to make a guess as to what the correct aspect ratio is?
  220.     NXSize tiffSize = {0.0, 0.0};
  221.     NXBitmapImageRep *tiff = nil;
  222.     
  223.     if (!readILBM (stream, &pic)) {
  224.     return nil;
  225.     }
  226.  
  227.     scrw = pic.bmhd.w;         /* Screen width in bits */
  228.     scrh = pic.bmhd.h;         /* Screen height in scanlines */
  229.     scrd = pic.bmhd.nPlanes;   /* Screen depth in bit planes */
  230.     actuald = scrd + ((pic.bmhd.masking == MASKEXPLICIT) ? 1 : 0);
  231.     scrc = pic.colorCount;     /* Screen colors in # of color registers */
  232.     
  233.     if (scrd > 8) {
  234.     return nil;
  235.     }
  236.   
  237.     /* Uncompress the IFF image */
  238.     
  239.     iffData = expandIFFBody (&pic.bmhd, pic.imageData);
  240.     free (pic.imageData);
  241.     pic.imageData = NULL;
  242.     
  243.     if (guessAspectRatio && adjustAspectRatio) {
  244.     int xGuess;
  245.     float aspect = (pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 0.0;
  246.     if ((pic.viewMode & HIRES) && !(pic.viewMode & LACE)) {
  247.         xGuess = 5;
  248.     } else if (!(pic.viewMode & HIRES) && (pic.viewMode & LACE)) {
  249.         xGuess = 20;
  250.     } else {
  251.         xGuess = 1&}
  252.     if (fabs((((float)xGuess) / 11.0) - aspect) > 0.001) {    // Might be wrong; fix it up...
  253.         pic.bmhd.xAspect = xGuess;
  254.         pic.bmhd.yAspect = 11;
  255.     }
  256.     }
  257.  
  258.     if (tiffSize.width < 1 || tiffSize.height < 1) {
  259.     int realWidth, realHeight;
  260.     float aspect = (adjustAspectRatio && pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 1.0;
  261.     if (adjustAspectRatio) {
  262.         realWidth = (aspect > 1.0) ? scrw : (scrw * aspect);
  263.         realHeight = (aspect < 1.0) ? scrh : (scrh / aspect);
  264.     } else {
  265.         realWidth = (aspect < 1.0) ? scrw : (scrw * aspect);
  266.         realHeight = (aspect > 1.0) ? scrh : (scrh / aspect);
  267.     }
  268.     if ((tiffSize.width < 1) && (tiffSize.height < 1)) {
  269.         tiffSize.width = realWidth;
  270.         tiffSize.height = realHeight;
  271.     } else if (tiffSize.width < 1) {
  272.         tiffSize.width = tiffSize.height * realWidth / realHeight;
  273.     } else {
  274.         tiffSize.height = tiffSize.width * realHeight / realWidth;
  275.     }
  276.     tiffSize.width = MAX(tiffSize.width, 1);
  277.     tiffSize.height = MAX(tiffSize.height, 1);
  278.     }
  279.  
  280.     {
  281.     unsigned char *scanLines[8], curMask;
  282.     unsigned short alpha;
  283.     unsigned long color;
  284.     int w, h, cnt, rshift, byte, reg;
  285.     int rowBytes = ((scrw + 7) / 8);
  286.     int ham, halfbrite;
  287.     
  288.     if (readMask && (pic.bmhd.masking == MASKEXPLICIT || pic.bmhd.masking == MASKTRANSPARENTCOLOR)) {
  289.         readMask = pic.bmhd.masking;
  290.     } else {
  291.         readMask = 0;
  292.     }
  293.         
  294.     halfbrite = pic.viewMode & EXTRA_HALFBRITE;
  295.     if (ham = ((pic.viewMode & HAM) != 0)) {
  296.         spp = 3;
  297.     } else {
  298.         spp = 1;
  299.         /* is the image grayscale? (for all colors in the palette, r == g == b?) */
  300.         for (cnt = 0; cnt < pic.colorCount; cnt++) {
  301.         color = pic.colorTable[cnt];
  302.         if ((((color >> 16) & 255) != (color & 255)) || (((color >> 16) & 255) != ((color >> 8) & 255))) {
  303.             spp = 3;
  304.             break;
  305.         }
  306.         }
  307.     }
  308.     spp += (readMask ? 1 : 0);
  309.     
  310.     bps = 4;
  311.     /* can the image be represented in 4-bits? */
  312.     for (cnt = 0; cnt < pic.colorCount; cnt++) {
  313.         color = pic.colorTable[cnt];
  314.         if ((pic.colorTable[cnt] & 0x00f0f0f0) != ((pic.colorTable[cnt] << 4) & 0x00f0f0f0)) {
  315.         bps = 8;
  316.         break;
  317.         }
  318.     }
  319.         
  320.     tiff = [[NXBitmapImageRep alloc] initData:NULL
  321.                         pixelsWide:scrw
  322.                         pixelsHigh:scrh
  323.                         bitsPerSample:bps
  324.                         samplesPerPixel:spp
  325.                         hasAlpha:((readMask != 0) ? YES : NO)
  326.                         isPlanar:NO
  327.                         colorSpace:(spp > 2) ? NX_RGBColorSpace : NX_OneIsWhiteColorSpace
  328.                         bytesPerRow:0
  329.                         bitsPerPixel:0];
  330.     [tiff setSize:&tiffSize];
  331.     tiffData = [tiff data];
  332.     
  333.     alpha = (bps == 8) ? 0x0ff : 0x0f;
  334.         
  335.     !(h = 0; h < scrh; h++) {
  336.     
  337.         for (cnt = 0; cnt < scrd; cnt++) {
  338.         scanLines[cnt] = iffData + rowBytes * ((actuald * h) + cnt);
  339.         }
  340.         
  341.         for (w = 0; w < scrw; w++) {
  342.         curMask = mask[w & 7];
  343.         rshift = 7 - (w & 7);
  344.         byte = w >> 3;
  345.         reg = 0;
  346.         for (cnt = 0; cnt < scrd; cnt++) {
  347.             reg += ((((*(scanLines[cnt] + byte)) & curMask) >> rshift) << cnt);
  348.         }
  349.         switch (readMask) {
  350.             case MASKNONE:
  351.             break;
  352.             case MASKEXPLICIT: 
  353.             if ((*(iffData + rowBytes * ((actuald * h) + scrd) + byte)) & curMask) {
  354.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  355.             } else {
  356.                 alpha = 0;
  357.             }
  358.             break;
  359.             case MASKTRANSPARENTCOLOR: 
  360.             if (reg == pic.bmhd.transparentColor) {
  361.                 alpha = 0;
  362.             } else {
  363.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  364.             }
  365.             break;
  366.         }
  367.     
  368.         if (ham) {
  369.             int regf = reg & 0x0f;
  370.             if (w == 0) {
  371.             color = pic.colorTable[0];
  372.             }
  373.             switch (reg & 0x030) {
  374.             case 0x000: color = pic.colorTable[reg]; break;
  375.             case 0x010: color = (color & 0x00ffff00) | (regf | (regf << 4)); break;
  376.             case 0x030: color = (color & 0x00ff00ff) | ((regf | (regf << 4)) << 8); break;  
  377.             case 0x020: color = (color & 0x0000ffff) | ((regf | (regf << 4)) << 16); break;  
  378.             }
  379.         } else if (halfbrite && (reg >= 32)) {
  380.             color = ((pic.colorTable[reg % 32]) >> 1) & 0x007f7f7f;
  381.         } else {
  382.             color = pic.colorTable[reg];
  383.         }
  384.     
  385.         if (!alpha) color = 0;
  386.     
  387.         if (bps == 8) {
  388.             if (spp == 1 || spp == 2) {
  389.             *tiffData++ = color & 0x0ff;
  390.             if (spp == 2) *tiffData++ = alpha;
  391.             } else if (spp == 3 || spp == 4) {
  392.             *tiffData++ = (color >> 16) & 0x0ff;
  393.             *tiffData++ = (color >> 8) & 0x0ff;
  394.             *tiffData++ = (color & 0x0ff);
  395.             if (spp == 4) *tiffData++ = alpha;
  396.             }
  397.         } else {    /* bps == 4 */
  398.             switch (spp) {
  399.             case 1:
  400.             if (w & 1) {    /* odd pixel */
  401.                 *tiffData |= (color & 0x0f);
  402.                 tiffData++;
  403.             } else {        /* even pixel */
  404.                 *tiffData = (color & 0x0f0);
  405.             }
  406.             break;
  407.             case 2:
  408.             *tiffData++ = (color & 0x0f0) | alpha;
  409.             break;
  410.             case 3:
  411.             if (w & 1) {    /* odd pixel */
  412.                 *tiffData |= (color >> 16) & 0x0f; /* the red */
  413.                 tiffData++;
  414.                 *tiffData++ = ((color >> 8) & 0xf0) | (color & 0x0f); /* green & blue */
  415.             } else {        /* even pixel */
  416.                 *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  417.                 *tiffData = (color & 0x0f0);
  418.             }
  419.     "eak;
  420.             case 4:
  421.             *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  422.             *tiffData++ = (color & 0x0f0) | alpha;
  423.             }
  424.         }
  425.         }
  426.     
  427.         if ((spp & 1) && (bps == 4) && (scrw & 1)) tiffData++;    /* We're stuck in mid byte! */
  428.     
  429.     }
  430.     }
  431.  
  432.     free(iffData);
  433.  
  434.     return tiff;
  435. }