home *** CD-ROM | disk | FTP | other *** search
/ ftp.sunet.sepub/pictures / 2014.11.ftp.sunet.se-pictures.tar / ftp.sunet.se / pub / pictures / ACiD-artpacks / programs / unix / editors / gimp-plugins-unstable-0_99_23_tar.gz / gimp-plugins-unstable-0_99_23_tar / gimp-plugins-unstable-0.99.23 / tdi / tdi.c < prev   
C/C++ Source or Header  |  1998-02-24  |  45KB  |  1,416 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * Alias|Wavefront IFF (TDI) image reading and writing code 
  4.  * Copyright (C) 1997 Mike Taylor
  5.  * (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  */
  22.  
  23. /*
  24.  * This loads and saves a version of the IFF (interchange file format) 
  25.  * image format.  This is a tile based image format used primarily by
  26.  * Alias|Wavefront (TDI) products.  
  27.  *
  28.  * This will not load the versions of the IFF format supported by the 
  29.  * Commodore Amiga.  If my inclusion of the (.iff) extension for my
  30.  * plug-in confuses any Amiga-ites or an ILBM iff reader wishes to use
  31.  * it, then let me know an I'll drop it from my plug-in.  (this wouldn't
  32.  * be a problem if gimp used magic cookies, but hey...)
  33.  *
  34.  * Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
  35.  */
  36.  
  37. /* Event history:
  38.  * V 1.0, MT, 18-dec-97: initial version of plug-in
  39.  */
  40.  
  41. /* Features
  42.  *  - loads and saves
  43.  *    - RGBA and RGB files 
  44.  *    - Z-Buffer info (read only)
  45.  *
  46.  * NOTE: z-buffer data is read into a separate layer.  z-buffer data is not 
  47.  *       written out though, so data can be lost in loading and saving a file.
  48.  *
  49.  */
  50.  
  51. static char ident[] = "@(#) GIMP Alias|Wavefront iff (TDI) image file-plugin v1.0  18-dec-97";
  52.  
  53. #include <stdlib.h>
  54. #include <stdio.h>
  55. #include <sys/types.h>
  56. #include <sys/stat.h>
  57. #include <gtk/gtk.h>
  58. #include <libgimp/gimp.h>
  59.  
  60. #include <assert.h>
  61.  
  62. /* #define IFF_DEBUG */
  63.  
  64. #ifdef IFF_DEBUG
  65. static FILE * debugFile = NULL; 
  66. #    define IFF_DEBUG_PRINT(a,b) fprintf(debugFile,a,b);fflush(debugFile)
  67. #else
  68. #    define NDEBUG
  69. #    define IFF_DEBUG_PRINT(a,b) 
  70. #endif
  71.  
  72. /***************
  73.  * Definitions *
  74.  ***************/
  75.  
  76. typedef struct {
  77.     guint32 tag;
  78.     guint32 start;
  79.     guint32 size;
  80.     guint32 chunkType;
  81. } iff_chunk;
  82.  
  83. #define RGB_FLAG     1
  84. #define ALPHA_FLAG   2
  85. #define ZBUFFER_FLAG 4
  86.  
  87. /**************
  88.  * Prototypes *
  89.  **************/
  90.  
  91. /* Standard Plug-in Functions */
  92.  
  93. static void   query ();
  94. static void   run   ( char *name, int nparams, GParam *param, 
  95.                       int *nreturn_vals, GParam **return_vals );
  96.  
  97. /* Local Helper Functions */ 
  98.  
  99. static gint32   load_image( char *filename );
  100. static gint     save_image( char *filename, gint32 image_ID, gint32 drawable_ID );
  101.  
  102. static guint16  get_short( FILE * file );
  103. static void     put_short( guint16 value, FILE * file );
  104. static guint32  get_long( FILE * file );
  105. static void     put_long( guint32 value, FILE * file );
  106. static gchar    get_char( FILE * file );
  107. static void     put_char( guchar value, FILE * file );
  108.  
  109. /******************
  110.  * Implementation *
  111.  ******************/
  112.  
  113. GPlugInInfo PLUG_IN_INFO =
  114. {
  115.   NULL,    /* init_proc */
  116.   NULL,    /* quit_proc */
  117.   query,   /* query_proc */
  118.   run,     /* run_proc */
  119. };
  120.  
  121. MAIN ()
  122.  
  123. static void query ()
  124. /*
  125.  * Description:
  126.  *     Register the services provided by this plug-in 
  127.  */
  128. {
  129.     static GParamDef load_args[] = {
  130.         { PARAM_INT32,  "run_mode", "Interactive, non-interactive" },
  131.         { PARAM_STRING, "filename", "The name of the file to load" },
  132.         { PARAM_STRING, "raw_filename", "The name entered" },
  133.     };
  134.     static GParamDef load_return_vals[] = {
  135.         { PARAM_IMAGE, "image", "Output image" },
  136.     };
  137.     static int nload_args = sizeof (load_args) / sizeof (load_args[0]);
  138.     static int nload_return_vals = sizeof (load_return_vals) / 
  139.                                    sizeof (load_return_vals[0]);
  140.  
  141.     static GParamDef save_args[] = {
  142.         { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
  143.         { PARAM_IMAGE, "image", "Input image" },
  144.         { PARAM_DRAWABLE, "drawable", "Drawable to save" },
  145.         { PARAM_STRING, "filename", "The name of the file to save the image in" },
  146.         { PARAM_STRING, "raw_filename",
  147.           "The name of the file to save the image in" }
  148.     };
  149.     static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  150.  
  151.     gimp_install_procedure ("file_iff_load",
  152.                             "loads files of the Alias|Wavefront iff (tdi) file format",
  153.                             "loads files of the Alias|Wavefront iff (tdi) file format",
  154.                             "Michael Taylor",
  155.                             "Michael Taylor",
  156.                             "1997",
  157.                             "<Load>/TDI",
  158.                             NULL,
  159.                             PROC_PLUG_IN,
  160.                             nload_args, nload_return_vals,
  161.                             load_args, load_return_vals);
  162.  
  163.   gimp_install_procedure ("file_iff_save",
  164.                           "save file in the Alias|Wavefront iff (tdi) file format",
  165.                           "save file in the Alias|Wavefront iff (tdi) file format",
  166.                           "Michael Taylor",
  167.                           "Michael Taylor",
  168.                           "1997",
  169.                           "<Save>/TDI",
  170.                           "RGB*, GRAY*",
  171.                           PROC_PLUG_IN,
  172.                           nsave_args, 0,
  173.                           save_args, NULL);
  174.  
  175.     gimp_register_load_handler ("file_iff_load", "iff,tdi", "");
  176.     gimp_register_save_handler ("file_iff_save", "iff,tdi", "");
  177. }
  178.  
  179. static void run ( char *name, int nparams, GParam *param, int *nreturn_vals,
  180.                   GParam **return_vals )
  181. /* 
  182.  *  Description:
  183.  *      perform registered plug-in function 
  184.  *
  185.  *  Arguments:
  186.  *      name         - name of the function to perform
  187.  *      nparams      - number of parameters passed to the function
  188.  *      param        - parameters passed to the function
  189.  *      nreturn_vals - number of parameters returned by the function
  190.  *      return_vals  - parameters returned by the function
  191.  */
  192. {
  193.     static GParam values[2];
  194.     GRunModeType run_mode;
  195.     GStatusType status = STATUS_SUCCESS;
  196.     gint32 image_ID;
  197.  
  198.  
  199.     run_mode = param[0].data.d_int32;
  200.  
  201.     *nreturn_vals = 1;
  202.     *return_vals = values;
  203.  
  204.     values[0].type = PARAM_STATUS;
  205.     values[0].data.d_status = STATUS_CALLING_ERROR;
  206.  
  207.     if (strcmp (name, "file_iff_load") == 0) {
  208.         /* Perform the image load */
  209.         image_ID = load_image (param[1].data.d_string);
  210.         if (image_ID != -1) {
  211.             /* The image load was successful */
  212.             *nreturn_vals = 2;
  213.             values[0].data.d_status = STATUS_SUCCESS;
  214.             values[1].type = PARAM_IMAGE;
  215.             values[1].data.d_image = image_ID;
  216.         } else {
  217.             /* The image load falied */
  218.             values[0].data.d_status = STATUS_EXECUTION_ERROR;
  219.         }
  220.     } else if (strcmp (name, "file_iff_save") == 0) {
  221.         if (status == STATUS_SUCCESS) {
  222.             if ( !save_image ( param[3].data.d_string, param[1].data.d_int32,
  223.                                param[2].data.d_int32))
  224.             {
  225.                 status = STATUS_EXECUTION_ERROR;
  226.             }
  227.         }
  228.         values[0].data.d_status = status;
  229.         
  230.     } else {
  231.         g_assert (FALSE);
  232.     }
  233. }
  234.  
  235.     
  236. static guchar get_char( FILE * file )
  237. /* 
  238.  * Description:
  239.  *     Reads an 8-bit character from a file
  240.  */
  241. {
  242.     guchar buf;
  243.     size_t result = 0;
  244.     
  245.     result = fread( &buf, 1, 1, file);
  246.     assert( result == 1 );
  247.     return buf;
  248. }    
  249.  
  250. static void put_char( guchar value, FILE * file )
  251. /* 
  252.  * Description:
  253.  *     Writes an 8-bit character to a file
  254.  */
  255. {
  256.     fwrite( &value, 1, 1, file);
  257. }
  258.     
  259. static guint16 get_short( FILE * file )
  260. /* 
  261.  * Description:
  262.  *     Reads a 16-bit integer from a file in such a way that the machine's
  263.  *     bit ordering should not matter 
  264.  */
  265. {
  266.     guchar buf[2];
  267.     size_t result = 0;
  268.     
  269.     result = fread( buf, 2, 1, file);
  270.     assert( result == 1 );
  271.     return ( buf[0] << 8 ) + ( buf[1] << 0 );
  272. }
  273.     
  274. static void put_short( guint16 value, FILE * file )
  275. /* 
  276.  * Description:
  277.  *     Writes a 16-bit integer from a file in such a way that the machine's
  278.  *     bit ordering should not matter 
  279.  */
  280. {
  281.     guchar buf[2];
  282.     buf[0] = (guchar)( value >> 8 );
  283.     buf[1] = (guchar)( value % 256 );
  284.  
  285.     fwrite( buf, 2, 1, file);
  286. }
  287.  
  288. static guint32 get_long( FILE * file )
  289. /* 
  290.  * Description:
  291.  *     Reads a 16-bit integer from a file in such a way that the machine's
  292.  *     bit ordering should not matter 
  293.  */
  294. {
  295.     guchar buf[4];
  296.     size_t result = 0;
  297.  
  298.     result = fread( buf, 4, 1, file );
  299.     assert( result == 1 );
  300.     return ( buf[0] << 24 ) + ( buf[1] << 16 ) 
  301.          + ( buf[2] << 8 ) + ( buf[3] << 0 );
  302. }
  303.     
  304. static void put_long( guint32 value, FILE * file )
  305. /* 
  306.  * Description:
  307.  *     Writes a 32-bit integer from a file in such a way that the machine's
  308.  *     bit ordering should not matter 
  309.  */
  310. {
  311.     guchar buf[4];
  312.     buf[0] = (guchar)( ( value >> 24 ) % 256 );
  313.     buf[1] = (guchar)( ( value >> 16 ) % 256 );
  314.     buf[2] = (guchar)( ( value >>  8 ) % 256 );
  315.     buf[3] = (guchar)( value % 256 );
  316.  
  317.     fwrite( buf, 4, 1, file);
  318. }
  319.  
  320.  
  321. /*************************
  322.  * IFF Chunking Routines *
  323.  *************************/
  324.  
  325. #define CHUNK_STACK_SIZE 32
  326.  
  327. static gint chunkDepth = -1;
  328. static iff_chunk chunkStack[CHUNK_STACK_SIZE];
  329.  
  330. static iff_chunk begin_read_chunk( FILE * file )
  331. /*
  332.  *  Description:
  333.  *      begin reading an iff chunk.  The file should be queued up to the 
  334.  *      start of the chunk 
  335.  * 
  336.  *  Arguments:
  337.  *      file      - the file to read from
  338.  *
  339.  *  Return Value:
  340.  *      record containing chunk info
  341.  *      
  342.  */
  343. {
  344.     chunkDepth++;
  345.     assert( chunkDepth < CHUNK_STACK_SIZE );
  346.     
  347.     chunkStack[chunkDepth].start = ftell( file );
  348.     chunkStack[chunkDepth].tag = get_long( file );
  349.     chunkStack[chunkDepth].size = get_long( file );
  350.     
  351.     if ( chunkStack[chunkDepth].tag == 'FOR4' ) {
  352.         /* We have a form, so read the form type tag as well */
  353.         chunkStack[chunkDepth].chunkType = get_long( file );
  354.     } else {
  355.         chunkStack[chunkDepth].chunkType = 0;
  356.     }
  357.     
  358.     IFF_DEBUG_PRINT("Beginning Chunk: %4s",(char*)(&chunkStack[chunkDepth].tag));
  359.     IFF_DEBUG_PRINT("  start: %lu", chunkStack[chunkDepth].start );
  360.     IFF_DEBUG_PRINT("  size: %lu", chunkStack[chunkDepth].size );
  361.     if ( chunkStack[chunkDepth].chunkType != 0 ) {
  362.         IFF_DEBUG_PRINT("  type: %4s",(char*)(&chunkStack[chunkDepth].chunkType));
  363.     }
  364.     IFF_DEBUG_PRINT("  depth: %d\n", chunkDepth );
  365.     return chunkStack[chunkDepth];
  366. }
  367.  
  368. static void end_read_chunk( FILE * file )
  369. /*
  370.  *  Description:
  371.  *      end reading an iff chunk.  Seeks to the end of the chunk.
  372.  * 
  373.  *  Arguments:
  374.  *      file      - the file to read from
  375.  */
  376. {
  377.     guint32 end = chunkStack[chunkDepth].start + chunkStack[chunkDepth].size + 8;
  378.     gint part;
  379.     
  380.     if ( chunkStack[chunkDepth].chunkType != 0 ) {
  381.         end += 4;
  382.     }
  383.     /* Add padding */
  384.     part = end % 4;
  385.     if ( part != 0 ) {
  386.         end += 4 - part;   
  387.     }
  388.     
  389.     fseek( file, end, SEEK_SET );
  390.     IFF_DEBUG_PRINT("Closing Chunk: %4s\n",(char*)(&chunkStack[chunkDepth].tag));
  391.     
  392.     chunkDepth--; 
  393.     assert( chunkDepth >= -1 );
  394. }
  395.  
  396. static void begin_write_chunk( FILE * file, guint32 tag, guint32 type )
  397. /*
  398.  *  Description:
  399.  *      begin writing an iff chunk.
  400.  * 
  401.  *  Arguments:
  402.  *      file      - the file to write to
  403.  *
  404.  *  Return Value:
  405.  *      record containing chunk info
  406.  *      
  407.  */
  408. {
  409.     chunkDepth++;
  410.     assert( chunkDepth < CHUNK_STACK_SIZE );
  411.     
  412.     chunkStack[chunkDepth].start = ftell( file );
  413.     chunkStack[chunkDepth].tag = tag;
  414.     chunkStack[chunkDepth].chunkType = type;
  415.     put_long( tag, file );
  416.     put_long( 0L, file ); /* Space for size info */
  417.     
  418.     if ( tag == 'FOR4' ) {
  419.         put_long( type, file );
  420.     }
  421.     
  422.     IFF_DEBUG_PRINT("Begin Writing Chunk: %4s",(char*)(&chunkStack[chunkDepth].tag));
  423.     IFF_DEBUG_PRINT("  start: %lu", chunkStack[chunkDepth].start );
  424.     if ( chunkStack[chunkDepth].chunkType != 0 ) {
  425.         IFF_DEBUG_PRINT("  type: %4s",(char*)(&chunkStack[chunkDepth].chunkType));
  426.     }
  427.     IFF_DEBUG_PRINT("  depth: %d\n", chunkDepth );
  428. }
  429.  
  430. static void end_write_chunk( FILE * file )
  431. /*
  432.  *  Description:
  433.  *      end writing an iff chunk.
  434.  * 
  435.  *  Arguments:
  436.  *      file      - the file to write to
  437.  *      
  438.  */
  439. {
  440.     guint32 end = ftell( file );
  441.     gint part, i;
  442.     
  443.     chunkStack[chunkDepth].size = ( end - chunkStack[chunkDepth].start ) - 8;
  444.     
  445.     /* Add padding */
  446.     part = end % 4;
  447.     if ( part != 0 ) {
  448.         for( i = 0; i < ( 4 - part ); i++ ) {
  449.             put_char( 0, file );
  450.         }
  451.         end += 4 - part;   
  452.         IFF_DEBUG_PRINT("Padding: %d\n",part);
  453.     }
  454.     
  455.     fseek( file, ( chunkStack[chunkDepth].start + 4 ), SEEK_SET );
  456.     put_long( chunkStack[chunkDepth].size, file );
  457.     fseek( file, end, SEEK_SET );
  458.    
  459.     IFF_DEBUG_PRINT("Closing Chunk: %4s\n",(char*)(&chunkStack[chunkDepth].tag));
  460.     IFF_DEBUG_PRINT("End: %u\n",end);
  461.     IFF_DEBUG_PRINT("End pos: %u\n",ftell(file));
  462.     IFF_DEBUG_PRINT("Size: %u\n",chunkStack[chunkDepth].size);
  463.     
  464.     chunkDepth--; 
  465.     assert( chunkDepth >= -1 );
  466. }
  467.  
  468. /************************
  469.  * Compression Routines *
  470.  ************************/
  471.     
  472. static guchar * decompress_rle( FILE * file, guint32 numBytes )
  473. /*
  474.  *  Description:
  475.  *      decompress rle encoded data
  476.  * 
  477.  *  Arguments:
  478.  *      file      - the file to read from
  479.  *      numBytes  - the number of bytes to decompress
  480.  *      
  481.  *  Return Value:
  482.  *      pointer to decompressed data (must be freed by client)
  483.  */
  484. {
  485.     guchar * data = g_new(guchar, numBytes );
  486.     guchar nextChar, count;
  487.     gint i;
  488.     guint32 byteCount = 0; 
  489.     
  490.     for ( i = 0; i<  numBytes; ++i ) {
  491.         data[i] = 0;
  492.     }  
  493.     
  494.     IFF_DEBUG_PRINT("Decompressing data %d\n",numBytes);
  495.     
  496.     while ( byteCount < numBytes ) {
  497.         nextChar = get_char( file );
  498.         count = (nextChar & 0x7f) + 1;
  499.         if ( ( byteCount + count ) > numBytes ) break;
  500.         if ( nextChar & 0x80 ) {
  501.             /* We have a duplication run */
  502.  
  503.             IFF_DEBUG_PRINT("duplicate run %d of ",(int)count);
  504.             IFF_DEBUG_PRINT("%2x\n",(int)nextChar);
  505.             
  506.             nextChar = get_char( file );
  507.             assert( ( byteCount + count ) <= numBytes ); 
  508.             for ( i = 0; i < count; ++i ) {
  509.                 data[byteCount] = nextChar;
  510.                 byteCount++;
  511.             }
  512.         } else {
  513.             /* We have a verbatim run */
  514.             IFF_DEBUG_PRINT("verbatim run %d\n",(int)count);
  515.             for ( i = 0; i < count; ++i ) {
  516.                 data[byteCount] = get_char( file );
  517.                 byteCount++;
  518.             }
  519.         }
  520.         assert( byteCount <= numBytes ); 
  521.     }
  522.     return data;
  523.     
  524. }
  525.  
  526. static guchar * decompress_tile_rle( FILE * file, guint16 width,  
  527.                                      guint16 height, guint16 depth )
  528. /*
  529.  *  Description:
  530.  *      decompress a tile of the image into a gimp compatible format
  531.  * 
  532.  *  Arguments:
  533.  *      file      - the file to read from
  534.  *      width     - width of the tile
  535.  *      height    - height of the tile
  536.  *      depth     - number of channels in the tile
  537.  *      
  538.  *  Return Value:
  539.  *      pointer to decompressed data (must be freed by client)
  540.  *      
  541.  */
  542. {
  543.     guchar * channels[4], * data;
  544.     gint i, j, k, row, column;
  545.     
  546.     IFF_DEBUG_PRINT("Decompressing tile [ %hd, ",width);
  547.     IFF_DEBUG_PRINT("%hd, ",height);
  548.     IFF_DEBUG_PRINT("%hd ]\n",depth);
  549.     
  550.     /* Decompress the different channels (RGBA) */
  551.     assert(depth<=4);
  552.     for ( i = ( depth - 1 ); i >= 0; --i ) {
  553.         IFF_DEBUG_PRINT( "Layer Data Begins at %u\n", ftell(file) );
  554.         channels[i] = decompress_rle( file, width * height );
  555.         IFF_DEBUG_PRINT( "Layer Data Ends at %u\n", ftell(file) );
  556.     }
  557.           
  558.     /* Merge the channels into the order gimp expects */ 
  559.     data = g_new(guchar, width * height * depth );
  560.     for ( row = 0; row < height; ++row ) {
  561.         i = ( height - row - 1 ) * width;
  562.         j = row * width * depth;
  563.         for ( column = 0; column < width; ++column ) {
  564.             for ( k = 0; k < depth; k++ ) {
  565.                 data[j] = channels[k][i];
  566.                 j++;
  567.             }
  568.             i++;
  569.         }
  570.     }
  571.     
  572.     for ( i = 0; i < depth; i++ ) {
  573.         free( channels[i] );
  574.     }
  575.     
  576.     return data;
  577. }
  578.  
  579. static guchar * read_uncompressed_tile( FILE * file, guint16 width,  
  580.                                         guint16 height, guint16 depth )
  581. /*
  582.  *  Description:
  583.  *      read a tile that is not compressed.  This occurs in the case where the
  584.  *      a tile has so much change in it that rle encoding only makes it larger.
  585.  * 
  586.  *  Arguments:
  587.  *      file      - the file to read from
  588.  *      width     - width of the tile
  589.  *      height    - height of the tile
  590.  *      depth     - number of channels in the tile
  591.  *      
  592.  *  Return Value:
  593.  *      pointer to pixel data (must be freed by client)
  594.  *      
  595.  */
  596. {
  597.     guchar * data = NULL; 
  598.     guchar pixel[4];
  599.     gint i, j, d, index;
  600.     data = g_new(guchar, width * height * depth );
  601.     
  602.     IFF_DEBUG_PRINT("Begin reading uncompressed tile\n",NULL);
  603.     
  604.     for ( i = ( height - 1 ); i >= 0; --i ) {  
  605.         index = i * width * depth;
  606.         for ( j = 0; j < width; ++j ) { 
  607.             fread( pixel, depth, 1, file ); 
  608.             for ( d = ( depth - 1 ); d >= 0; --d ) { 
  609.                 data[index] = pixel[d];
  610.                 ++index;
  611.             }
  612.         }
  613.     }
  614.     IFF_DEBUG_PRINT("End reading uncompressed tile\n",NULL);
  615.     
  616.     return data;
  617. }
  618.  
  619. static guint32 compress_rle( guchar * data, guint32 numBytes,
  620.                              guchar * dest, guint32 destSize )
  621. /*
  622.  *  Description:
  623.  *      compress data using rle encoding and write it to a buffer.  If the
  624.  *      data will not fit into the buffer then 0 is returned
  625.  * 
  626.  *  Arguments:
  627.  *      data      - the data to compress
  628.  *      numBytes  - the number of bytes to compress
  629.  *      dest      - buffer to encode into
  630.  *      destSize  - size of buffer
  631.  * 
  632.  *  Return Value:
  633.  *      size of the compressed data (0 if failed)
  634.  */
  635.  {
  636.     gint state = 0;
  637.     gint index = 0;
  638.     gint runCount, i, last;
  639.     guchar nextChar, firstChar, lastChar, run[128];
  640.     guint32 destIndex = 0; 
  641.     
  642.     /* This process is implemented with a finite state machine with
  643.      * the following states:
  644.      *   0 - virgin state...beginning new run
  645.      *   1 - have first character for next run
  646.      *   2 - processing duplicate run
  647.      *   3 - processing verbatim run
  648.      *   4 - in verbatim run, have found two in a row that match
  649.      */
  650.     while ( index < numBytes ) {
  651.         nextChar = data[index];
  652.         
  653.         /* Automatic failure if we run out of destination buffer space */
  654.         /* We need at least two bytes to write another record */
  655.         if ( ( destIndex +2 ) > destSize );
  656.         
  657.         switch ( state ) {
  658.             case 0:
  659.                 firstChar = nextChar;
  660.                 state = 1;
  661.                 break;
  662.             case 1:
  663.                 if ( nextChar == firstChar ) {
  664.                     /* beginning duplicate run */
  665.                     state = 2;
  666.                     runCount = 2;
  667.                 } else {
  668.                     /* beginning verbatim run */
  669.                     state = 3;
  670.                     runCount = 2;
  671.                     lastChar = nextChar;
  672.                     run[0] = firstChar;
  673.                     run[1] = nextChar;
  674.                 }
  675.                 break;
  676.             case 2:
  677.                 if ( ( nextChar == firstChar ) && ( runCount < 128 ) ) {
  678.                     runCount++;
  679.                 } else {
  680.                     /* run has ended, write if to the file */
  681.                     dest[destIndex++] = 0x80 | (guchar)( runCount - 1 );
  682.                     dest[destIndex++] = firstChar;
  683.                     
  684.                     IFF_DEBUG_PRINT("duplicate run %d of ",runCount);
  685.                     IFF_DEBUG_PRINT("%2x\n",(int)firstChar);
  686.                     /* nextChar is start of the next run */
  687.                     state = 1;
  688.                     firstChar = nextChar;
  689.                 }
  690.                 break;
  691.             case 3:
  692.                 if ( ( nextChar != lastChar ) && ( runCount < 128 ) ) {
  693.                     run[runCount] = nextChar;
  694.                     lastChar = nextChar;
  695.                     runCount++;
  696.                 } else if ( runCount == 127 ) {
  697.                     /* Always better to switch states in this case */
  698.                     /* Found duplicates, end verbatim run */
  699.                     if ( ( destIndex + runCount - 1 ) > destSize ) return 0;
  700.                     dest[destIndex++] = (guchar)( runCount - 2 );
  701.                     last = runCount - 1;
  702.                     for ( i = 0; i < last; ++i ) {
  703.                         dest[destIndex++] = run[i];
  704.                     }
  705.                     IFF_DEBUG_PRINT("verbatim run %d\n",(runCount-1));
  706.                     /* nextChar is start of the next run */
  707.                     state = 2;
  708.                     firstChar = nextChar;
  709.                     runCount = 2;
  710.                 } else if ( runCount < 128 ) {
  711.                     /* Found duplicates, possibly end verbatim run */
  712.                     run[runCount] = nextChar;
  713.                     lastChar = nextChar;
  714.                     runCount++;
  715.                     state = 4;
  716.                 } else {
  717.                     /* Hit maximum run size */
  718.                     /* Found duplicates, end verbatim run */
  719.                     if ( ( destIndex + runCount ) > destSize ) return 0;
  720.                     dest[destIndex++] = (guchar)( runCount - 1 );
  721.                     for ( i = 0; i < runCount; ++i ) {
  722.                         dest[destIndex++] = run[i];
  723.                     }
  724.                     IFF_DEBUG_PRINT("verbatim run %d\n",runCount);
  725.                     /* nextChar is start of the next run */
  726.                     state = 1;
  727.                     firstChar = nextChar;
  728.                 }
  729.                 break;
  730.             case 4:
  731.                 if ( nextChar != lastChar ) {
  732.                     /* Only run of two, need three to start a duplicate run */
  733.                     run[runCount] = nextChar;
  734.                     lastChar = nextChar;
  735.                     runCount++;
  736.                     state = 3;
  737.                 } else {
  738.                     /* We have three in a row, start a duplicate run */
  739.                     if ( ( destIndex + runCount - 2 ) > destSize ) return 0;
  740.                     dest[destIndex++] = (guchar)( runCount - 3 );
  741.                     last = runCount - 2;
  742.                     for ( i = 0; i < last; ++i ) {
  743.                         dest[destIndex++] = run[i];
  744.                     }
  745.                     IFF_DEBUG_PRINT("verbatim run %d\n",(runCount-2));
  746.                     /* nextChar is start of the next run */
  747.                     state = 2;
  748.                     firstChar = nextChar;
  749.                     runCount = 3;
  750.                 } 
  751.                 break;
  752.         }
  753.         index++;
  754.     }
  755.     
  756.     if ( ( destIndex + 2 ) > destSize ) return 0;
  757.     
  758.     /* clean up at end of input */
  759.     switch ( state ) {
  760.         case 1:
  761.             dest[destIndex++] = 0;
  762.             dest[destIndex++] = firstChar;
  763.             break;
  764.         case 2:
  765.             /* run has ended, write if to the file */
  766.             dest[destIndex++] = 0x80 | (guchar)( runCount - 1 );
  767.             dest[destIndex++] = firstChar;
  768.             break;
  769.         case 3:
  770.         case 4:
  771.             if ( ( destIndex + runCount ) > destSize ) return 0;
  772.             dest[destIndex++] = (guchar)( runCount - 1 );
  773.             for ( i = 0; i < runCount; ++i ) {
  774.                 dest[destIndex++] = run[i];
  775.             }
  776.             break;
  777.         default:
  778.             assert(FALSE);
  779.     }
  780.     
  781.     /* Make sure at least some compression is achieved */
  782.     if ( destIndex >= destSize ) return 0;
  783.     
  784.     return destIndex;
  785. }
  786.  
  787. static void write_uncompressed_tile( FILE * file, guchar * data, 
  788.                                          guint16 width, guint16 height, 
  789.                                          guint16 depth )
  790. /*
  791.  *  Description:
  792.  *      write an uncompressed tile.  This makes sense in the cases where 
  793.  *      the data has no runs in it and rle encoding actually makes the data
  794.  *      larger.
  795.  * 
  796.  *  Arguments:
  797.  *      file      - the file to write to
  798.  *      data      - data in gimp format
  799.  *      width     - width of the tile
  800.  *      height    - height of the tile
  801.  *      depth     - number of channels in the tile
  802.  *      
  803.  */
  804.     guchar pixel[4];
  805.     gint i, j, d, index;
  806.     
  807.     IFF_DEBUG_PRINT("Begin writing uncompressed tile\n",NULL);
  808.     
  809.     for ( i = ( height - 1 ); i >= 0; --i ) {  
  810.         index = i * width * depth;
  811.         for ( j = 0; j < width; ++j ) {  
  812.             for ( d = ( depth - 1 ); d >= 0; --d ) { 
  813.                 pixel[d] = data[index];
  814.                 ++index;
  815.             }
  816.             fwrite( pixel, depth, 1, file ); 
  817.         }
  818.     }
  819.     
  820.     IFF_DEBUG_PRINT("End writing uncompressed tile\n",NULL);
  821. }
  822.  
  823. static void compress_tile_rle( FILE * file, guchar * data, guint16 width,  
  824.                                guint16 height, guint16 depth )
  825. /*
  826.  *  Description:
  827.  *      Compress a tile into the given file.
  828.  * 
  829.  *  Arguments:
  830.  *      file      - the file to write too
  831.  *      data      - data in gimp format
  832.  *      width     - width of the tile
  833.  *      height    - height of the tile
  834.  *      depth     - number of channels in the tile
  835.  *      
  836.  */
  837. {
  838.     guchar * cdata = NULL; 
  839.     guchar * tile = NULL; 
  840.     guint32 cdataIndex = 0;
  841.     guint32 size, maxSize;
  842.     gint i, j, d, index;
  843.     
  844.     tile = g_new( guchar, width * height );
  845.     
  846.     /* The data will never be larger than this */
  847.     maxSize = width * height * depth;
  848.     cdata = g_new(guchar, maxSize );
  849.           
  850.     for ( d = ( depth - 1 ); d >= 0; --d ) {
  851.         index = 0;
  852.         for ( i = ( height - 1 ); i >= 0; --i ) {
  853.             for ( j = 0; j < width; ++j ) {
  854.                 tile[index] = data[ ( ( ( i * width ) + j ) * depth ) + d];
  855.                 index++;
  856.             }
  857.         }
  858.         IFF_DEBUG_PRINT( "Layer Data Begins at %u\n", ftell(file) );
  859.         size = compress_rle( tile, ( height * width ), 
  860.                              cdata + cdataIndex, maxSize - cdataIndex );
  861.         if ( size == 0 ) {
  862.             /* compressed data is larger than the original */
  863.             write_uncompressed_tile( file, data, width, height, depth );
  864.             return;
  865.         }
  866.         IFF_DEBUG_PRINT( "Layer Data Ends at %u\n", ftell(file) );
  867.         cdataIndex += size;
  868.     }
  869.     fwrite( cdata, cdataIndex, 1, file );
  870.     free( tile );
  871.     free( cdata );
  872. }   
  873.  
  874.  
  875.  
  876. /****************************
  877.  * Main Plug-in IO Routines *
  878.  ****************************/
  879.  
  880. static gint32 load_image (char *filename)
  881. /*
  882.  *  Description:
  883.  *      load the given image into gimp
  884.  * 
  885.  *  Arguments:
  886.  *      filename      - name on the file to read
  887.  *
  888.  *  Return Value:
  889.  *      Image id for the loaded image  
  890.  *      
  891.  */
  892. {
  893.     gint i, j, tile_height, row;
  894.     gint        result = -1;
  895.     FILE      * file = NULL;
  896.     gint32    * offsetTable = NULL;
  897.     gint32    * lengthTable = NULL;
  898.     gchar     * progMessage;
  899.     guchar    * dest; 
  900.     guchar    * dest_base;
  901.     GDrawable * drawable;
  902.     GDrawable * z_drawable;
  903.     gint32      image_ID;
  904.     gint32      layer_ID, z_layer_ID;
  905.     GPixelRgn   pixel_rgn, z_pixel_rgn;
  906.     iff_chunk   chunkInfo;
  907.     
  908.     /* Header info */
  909.     guint32     width, height, flags, compress;
  910.     guint16     tiles, depth;
  911.     
  912.     GImageType  imgtype;
  913.     GDrawableType gdtype;
  914.     
  915. #ifdef IFF_DEBUG    
  916.     debugFile = fopen("IffDebug.txt","w");
  917. #endif
  918.  
  919.     /* Set up progress display */
  920.     progMessage = malloc (strlen (filename) + 12);
  921.     if (!progMessage) gimp_quit ();
  922.     sprintf (progMessage, "Loading %s:", filename);
  923.     gimp_progress_init (progMessage);
  924.     free (progMessage);
  925.  
  926.     IFF_DEBUG_PRINT("Opening file: %s\n",filename);
  927.  
  928.     /* Open the file */
  929.     file = fopen( filename, "r" );
  930.     if ( NULL == file ) {
  931.         return -1;
  932.     }
  933.  
  934.     /* File should begin with a FOR4 chunk of type CIMG */
  935.     chunkInfo = begin_read_chunk( file );
  936.     if ( chunkInfo.chunkType != 'CIMG' ) {
  937.         return -1;
  938.     }
  939.     IFF_DEBUG_PRINT("Magic cookie found\n",NULL);
  940.     
  941.     /* Read the image header */
  942.     while ( TRUE ) {
  943.         guint32 blocklen;
  944.         chunkInfo = begin_read_chunk( file );
  945.                 
  946.         if ( chunkInfo.tag == 'TBHD' ) {
  947.             /* Header chunk found */
  948.             width = get_long( file );
  949.             height = get_long( file );
  950.             get_short( file ); /* Don't support */
  951.             get_short( file ); /* Don't support */
  952.             flags = get_long( file );
  953.             get_short( file ); /* Don't support */
  954.             tiles = get_short( file );
  955.             compress = get_long( file );
  956.             
  957.             IFF_DEBUG_PRINT("Width: %u\n",width);
  958.             IFF_DEBUG_PRINT("Height: %u\n",height);
  959.             IFF_DEBUG_PRINT("flags: %u\n",flags);
  960.             IFF_DEBUG_PRINT("tiles: %hu\n",tiles);
  961.             IFF_DEBUG_PRINT("compress: %u\n",compress);
  962.             end_read_chunk( file );    
  963.             
  964.             if ( compress != 1 ) {
  965.                 g_print("Unsupported Compression Type\n");
  966.                 return -1;
  967.             }
  968.             
  969.             break;
  970.         } else {
  971.             /* Skipping unused data */
  972.             IFF_DEBUG_PRINT("Skipping chunk: %4s\n",(char*)&(chunkInfo.tag));
  973.             end_read_chunk( file );
  974.         }
  975.     }
  976.     
  977.     depth = 0;
  978.     
  979.     if ( flags & RGB_FLAG ) {
  980.         depth += 3;
  981.     } 
  982.     if ( flags & ALPHA_FLAG ) {
  983.         depth += 1;
  984.     }
  985.     
  986.     switch ( depth ) {
  987.         case 1:
  988.             /* Just alpha or zbuffer data.  Load as grey scale */
  989.             imgtype = GRAY;
  990.             gdtype = GRAY_IMAGE;
  991.             break;
  992.         case 3:
  993.             /* Just RGB data. */
  994.             imgtype = RGB;
  995.             gdtype = RGB_IMAGE;
  996.             break;
  997.         case 4:
  998.         case 0:
  999.             /* RGB data with alpha. */
  1000.             imgtype = RGB;
  1001.             gdtype = RGBA_IMAGE;
  1002.             break;
  1003.     }
  1004.             
  1005.     image_ID = gimp_image_new ( width, height, imgtype );
  1006.     gimp_image_set_filename ( image_ID, filename );
  1007.     
  1008.     if ( depth > 0 ) {
  1009.         layer_ID = gimp_layer_new ( image_ID, "Background",
  1010.                                     width,
  1011.                                     height,
  1012.                                     gdtype, 100, NORMAL_MODE );
  1013.         gimp_image_add_layer ( image_ID, layer_ID, 0);
  1014.         drawable = gimp_drawable_get ( layer_ID );
  1015.         gimp_pixel_rgn_init ( &pixel_rgn, drawable, 0, 0, drawable->width,
  1016.                               drawable->height, TRUE, FALSE );
  1017.     } 
  1018.      
  1019.     if ( flags & ZBUFFER_FLAG ) {
  1020.         z_layer_ID = gimp_layer_new ( image_ID, "Z-Buffer",
  1021.                                       width,
  1022.                                       height,
  1023.                                       RGBA_IMAGE, 100, NORMAL_MODE );
  1024.         gimp_image_add_layer ( image_ID, z_layer_ID, 1);
  1025.         z_drawable = gimp_drawable_get ( z_layer_ID );
  1026.         gimp_pixel_rgn_init ( &z_pixel_rgn, z_drawable, 0, 0, z_drawable->width,
  1027.                               z_drawable->height, TRUE, FALSE );
  1028.            
  1029.     } 
  1030.     
  1031.     /* Read the tiled image data */
  1032.     while ( TRUE ) {
  1033.         guint32 blocklen;
  1034.         guint16 x1, x2, y1, y2, reverse_y, tile_width, tile_height, tile_area;
  1035.         chunkInfo = begin_read_chunk( file );
  1036.                
  1037.         if ( chunkInfo.chunkType == 'TBMP' ) {
  1038.             /* Image data found */
  1039.             guint16 x1, x2, y1, y2, reverse_y, tile_width, tile_height;
  1040.             guint32 zmin, zmax;
  1041.             gdouble znormalfactor;
  1042.             guint32 tile = 0;
  1043.             guint32 ztile = 0;
  1044.             size_t fpos;
  1045.             gboolean is_zbuffer_normization_pass, iscompressed;
  1046.             guchar * zdata, * tileData;
  1047.             
  1048.             IFF_DEBUG_PRINT("Reading image tiles\n",NULL);
  1049.             
  1050.             /* Do initialization of zbuffer info */
  1051.             if ( !( flags & ZBUFFER_FLAG ) ) {
  1052.                 /* No zbuffer data */
  1053.                 ztile = tiles;
  1054.                 is_zbuffer_normization_pass = FALSE;
  1055.             } else {
  1056.                 /* Initialize for normalization path */
  1057.                 zmin = ~0; zmax = 0;
  1058.                 fpos = ftell( file );
  1059.                 is_zbuffer_normization_pass = TRUE;
  1060.             }  
  1061.             
  1062.             if ( depth == 0 ) tile = tiles;
  1063.             
  1064.             /* Read tiles */
  1065.             while( ( tile < tiles ) || ( ztile < tiles ) ) {
  1066.                 chunkInfo = begin_read_chunk( file );
  1067.                 assert(chunkInfo.tag=='RGBA'||chunkInfo.tag=='ZBUF');
  1068.                 
  1069.                 /* Get tile size and location info */
  1070.                 x1 = get_short( file );
  1071.                 y1 = get_short( file );
  1072.                 x2 = get_short( file );
  1073.                 y2 = get_short( file );
  1074.                 tile_width = x2 - x1 + 1;
  1075.                 tile_height = y2 - y1 + 1;
  1076.                 tile_area = tile_width * tile_height;
  1077.                 IFF_DEBUG_PRINT("Tile x1: %hu  ",x1);
  1078.                 IFF_DEBUG_PRINT("y1: %hu  ",y1);
  1079.                 IFF_DEBUG_PRINT("x2: %hu  ",x2);
  1080.                 IFF_DEBUG_PRINT("y2: %hu\n",y2);
  1081.                 
  1082.                 if ( chunkInfo.size >= 
  1083.                      ( tile_width * tile_height * depth + 8 ) ) {
  1084.                     /* Compression was not used for this tile */
  1085.                     iscompressed = FALSE;
  1086.                 } else {
  1087.                     iscompressed = TRUE;   
  1088.                 }
  1089.                 
  1090.                 if ( is_zbuffer_normization_pass ) {
  1091.                     /* File actually stores 24-bits worth of data for Z, so to 
  1092.                      * use as much as we can in an 8-bit buffer, we'll 
  1093.                      * normalize it 
  1094.                      */
  1095.                     if ( chunkInfo.tag != 'ZBUF' ) {
  1096.                         end_read_chunk( file );
  1097.                         continue;
  1098.                     }
  1099.                     if ( iscompressed ) {
  1100.                         zdata = decompress_tile_rle( file, tile_width, 
  1101.                                                      tile_height, 4 );
  1102.                     } else {
  1103.                         zdata = read_uncompressed_tile( file, tile_width, 
  1104.                                                         tile_height, 4 );
  1105.                     }
  1106.                     
  1107.                     /* Scan the data looking for new max/min values */
  1108.                     for ( j = 0; j < tile_area; ++j ) {
  1109.                         guint32 value, index;
  1110.                         index = ( j * 4 );
  1111.                         
  1112.                         if ( zdata[index+3] > 0 ) {
  1113.                             /* Pixel has Z value */
  1114.                             value = ( zdata[index+2] << 16 ) +
  1115.                                     ( zdata[index+1] << 8 ) +
  1116.                                     ( zdata[index] << 0 );
  1117.                             zmin = MIN(zmin,value);
  1118.                             zmax = MAX(zmax,value);
  1119.                         }
  1120.                     }
  1121.                     free( zdata );
  1122.                     end_read_chunk( file );
  1123.                     ztile++;
  1124.                     if ( ztile >= tiles ) {
  1125.                         /* Finished normalization pass, reset to start */
  1126.                         ztile = 0;
  1127.                         is_zbuffer_normization_pass = FALSE;
  1128.                         znormalfactor = (gdouble)( zmax - zmin ) / 256.0;
  1129.                         fseek( file, fpos, SEEK_SET );
  1130.                         
  1131.                         IFF_DEBUG_PRINT("Z-Buffer Range [ %f, ",
  1132.                                         ((double)zmin/0.16777216e8));
  1133.                         IFF_DEBUG_PRINT("%f ]\n",
  1134.                                         ((double)zmax/0.16777216e8));
  1135.                     }
  1136.                     continue;
  1137.                 } 
  1138.                            
  1139.                 if ( chunkInfo.tag == 'RGBA' ) {
  1140.                     /* We have an RGBA chunk */
  1141.                     if ( depth == 0 ) {
  1142.                         end_read_chunk( file );
  1143.                         continue;
  1144.                     }
  1145.                     if ( iscompressed ) {
  1146.                         tileData = decompress_tile_rle( file, tile_width,
  1147.                                                         tile_height, depth );
  1148.                     } else {
  1149.                         tileData = read_uncompressed_tile( file, tile_width,
  1150.                                                            tile_height, depth );
  1151.                     }
  1152.  
  1153.                     if ( NULL != tileData ) {
  1154.                         /* Format reverses y coord compared to gimp */
  1155.                         reverse_y = ( height - y1 ) - tile_height;
  1156.  
  1157.                         gimp_pixel_rgn_set_rect( &pixel_rgn, tileData, x1,
  1158.                                                  reverse_y,
  1159.                                                  tile_width, tile_height );
  1160.                         free( tileData );
  1161.                     }
  1162.                     end_read_chunk( file );
  1163.                     tile++;
  1164.                 } else if ( chunkInfo.tag == 'ZBUF' ) {
  1165.                     /* We have an ZBUF chunk */
  1166.                     if ( iscompressed ) {
  1167.                         tileData = decompress_tile_rle( file, tile_width,  
  1168.                                                         tile_height, 4 );
  1169.                     } else {
  1170.                         tileData = read_uncompressed_tile( file, tile_width,  
  1171.                                                            tile_height, 4 );
  1172.                     }
  1173.                     for ( j = 0; j < tile_area; ++j ) {
  1174.                         guint32 value, index;
  1175.                         guchar normalized;
  1176.                         index = ( j * 4 );
  1177.                         
  1178.                         if ( tileData[index+3] > 0 ) {
  1179.                             value = ( tileData[index+2] << 16 ) +
  1180.                                     ( tileData[index+1] << 8 ) +
  1181.                                     ( tileData[index] << 0 );
  1182.                         
  1183.                             /* normalize value */
  1184.                             assert((value>=zmin)&&(value<=zmax));
  1185.                             
  1186.                             normalized = (guchar)( (gdouble)( value - zmin ) / 
  1187.                                                    znormalfactor );
  1188.                             tileData[index+3] = 0xff;
  1189.                         } else {
  1190.                             normalized = 0;
  1191.                             tileData[index+3] = 0x0;
  1192.                         }
  1193.                         tileData[index] = normalized;
  1194.                         tileData[index+1] = normalized;
  1195.                         tileData[index+2] = normalized;
  1196.                     }
  1197.                     
  1198.                     if ( NULL != tileData ) {
  1199.                         /* Format reverses y coord compared to gimp */
  1200.                         reverse_y = ( height - y1 ) - tile_height;
  1201.  
  1202.                         gimp_pixel_rgn_set_rect( &z_pixel_rgn, tileData, x1,
  1203.                                                  reverse_y,
  1204.                                                  tile_width, tile_height );
  1205.                         free( tileData );
  1206.                     }
  1207.                     end_read_chunk( file );
  1208.                     ztile++;
  1209.                 }
  1210.  
  1211.                 gimp_progress_update ((double)(MIN(tile,ztile)) / (double) tiles);
  1212.             }
  1213.             end_read_chunk( file ); /* End TBMP */
  1214.  
  1215.             if ( depth > 0 ) {
  1216.                 gimp_drawable_flush (drawable);
  1217.                 gimp_drawable_detach (drawable);
  1218.             }
  1219.             if ( flags & ZBUFFER_FLAG ) {
  1220.                 gimp_drawable_flush (z_drawable);
  1221.                 gimp_drawable_detach (z_drawable);
  1222.             } 
  1223.  
  1224.             break;
  1225.         } else {
  1226.             /* Skipping unused data */
  1227.             IFF_DEBUG_PRINT("Skipping chunk: %4s\n",(char*)&(chunkInfo.tag));
  1228.             end_read_chunk( file );
  1229.         }
  1230.     }
  1231.     
  1232.     end_read_chunk( file ); /* End FORM4 */
  1233.     
  1234.             
  1235.     fclose( file );
  1236.     
  1237.     IFF_DEBUG_PRINT("Returning image ID: %d\n",image_ID);
  1238.     
  1239.     
  1240. #ifdef IFF_DEBUG    
  1241.     fclose(debugFile);
  1242. #endif
  1243.     
  1244.     return image_ID;
  1245. }
  1246.  
  1247.  
  1248. static gint save_image( char *filename,  gint32 image_ID, gint32 drawable_ID )
  1249. /* 
  1250.  *  Description:
  1251.  *      save the given file out as an alias IFF or matte file
  1252.  * 
  1253.  *  Arguments: 
  1254.  *      filename    - name of file to save to
  1255.  *      image_ID    - ID of image to save
  1256.  *      drawable_ID - current drawable
  1257.  */
  1258. {
  1259.     gint        depth, i, j, d, index, gindex, row, column, tile_x, tile_y;
  1260.     gboolean    savingAlpha = FALSE;
  1261.     guchar    * src; 
  1262.     gchar     * progMessage;
  1263.     GDrawable * drawable;
  1264.     GPixelRgn   pixel_rgn;
  1265.     FILE      * file;
  1266.     guint32     flags, tile_size, tiles_in_x, tiles_in_y, tiles;
  1267.     guint32     tile_width, tile_height;
  1268.     guint16     x1, y1, x2, y2;
  1269.  
  1270. #ifdef IFF_DEBUG    
  1271.     debugFile = fopen("IffDebug.txt","w");
  1272. #endif
  1273.     
  1274.     /* Get info about image */
  1275.     drawable = gimp_drawable_get(drawable_ID);
  1276.  
  1277.     gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  1278.                         drawable->height, FALSE, FALSE);
  1279.  
  1280.     switch (gimp_drawable_type(drawable_ID))
  1281.     {
  1282.         case GRAY_IMAGE:
  1283.         case GRAYA_IMAGE:
  1284.             g_print("Greyscale images are not supported\n");
  1285.             return (FALSE);
  1286.         case RGB_IMAGE:
  1287.             depth = 3;
  1288.             flags = RGB_FLAG;
  1289.             break;
  1290.         case RGBA_IMAGE:
  1291.             flags = RGB_FLAG | ALPHA_FLAG;
  1292.             depth = 4;
  1293.             break;
  1294.         default:
  1295.             return FALSE;
  1296.     };
  1297.  
  1298.     
  1299.     /* Open the output file. */
  1300.     file = fopen (filename, "wb");
  1301.     if ( !file ) {
  1302.         return (FALSE);
  1303.     }
  1304.  
  1305.     /* Set up progress display */
  1306.     progMessage = malloc (strlen (filename) + 12);
  1307.     if (!progMessage) gimp_quit ();
  1308.     sprintf (progMessage, "Saving %s:", filename);
  1309.     gimp_progress_init (progMessage);
  1310.     free (progMessage);
  1311.  
  1312.     begin_write_chunk( file, 'FOR4', 'CIMG' );
  1313.     
  1314.     /* Write version number */
  1315.     begin_write_chunk( file, 'FVER', 0L );
  1316.     put_short( 1, file ); 
  1317.     put_short( 1, file ); 
  1318.     end_write_chunk( file );
  1319.     
  1320.     /* Write the image header */
  1321.     begin_write_chunk( file, 'TBHD', 0L );
  1322.     
  1323.     IFF_DEBUG_PRINT( "Width %hu\n", drawable->width );
  1324.     IFF_DEBUG_PRINT( "Height %hu\n", drawable->height );
  1325.     put_long( (guint32)(drawable->width), file );
  1326.     put_long( (guint32)(drawable->height), file );
  1327.     put_short( 1, file ); /* don't support */
  1328.     put_short( 1, file ); /* don't support */
  1329.     put_long( flags, file );
  1330.     put_short( 0, file ); /* don't support */
  1331.     
  1332.     /* Use the gimp's tile size for efficiency and simplicity */
  1333.     tile_size = gimp_tile_height();
  1334.     IFF_DEBUG_PRINT( "Tile size: %u\n", tile_size );
  1335.     tiles_in_x = ( drawable->width + ( tile_size - 1 ) ) / tile_size;
  1336.     tiles_in_y = ( drawable->height + ( tile_size - 1 ) ) / tile_size;
  1337.     tiles = tiles_in_x * tiles_in_y;
  1338.     put_short( (guint16)tiles, file );
  1339.     
  1340.     put_long( 1, file ); /* compression type */
  1341.     put_long( 0, file ); /* don't support */
  1342.     put_long( 0, file ); /* don't support */
  1343.     
  1344.     end_write_chunk( file ); /* BMHD */
  1345.     /* End Header */
  1346.     
  1347.     /* Write Fields Block */
  1348.     begin_write_chunk( file, 'FLDS', 0L );
  1349.     begin_write_chunk( file, 0x464d5401, 0L );
  1350.     put_long( 0x53474900, file );
  1351.     end_write_chunk( file ); /* FMT */
  1352.     put_long( 0L, file );
  1353.     put_long( 0L, file );
  1354.     end_write_chunk( file ); /* FLDS */
  1355.    
  1356.     /* Write Image Tiles */
  1357.     begin_write_chunk( file, 'FOR4', 'TBMP' );
  1358.     
  1359.     src = g_new( guchar, tile_size * tile_size * depth );
  1360.     
  1361.     for ( row = ( tiles_in_y - 1 ); row >= 0; --row ) {
  1362.         for ( column = 0; column < tiles_in_x; column++ ) {
  1363.             tile_x = column * tile_size;
  1364.             tile_width = MIN( tile_size, ( drawable->width - tile_x ) );
  1365.  
  1366.             tile_y = drawable->height - ( ( tiles_in_y - row ) * tile_size );
  1367.             tile_y = MAX( tile_y, 0 );
  1368.             if ( ( row == 0 ) && ( ( drawable->height % tile_size ) != 0 ) ) {
  1369.                 tile_height = drawable->height % tile_size;
  1370.             } else {
  1371.                 tile_height = tile_size;
  1372.             }
  1373.             
  1374.             IFF_DEBUG_PRINT( "Writing Tile - x: %d  ",(int)tile_x);
  1375.             IFF_DEBUG_PRINT( "y: %d  ",(int)tile_y);
  1376.             IFF_DEBUG_PRINT( "width: %d  ",(int)tile_width);
  1377.             IFF_DEBUG_PRINT( "height: %d\n",(int)tile_height);
  1378.             
  1379.             gimp_pixel_rgn_get_rect( &pixel_rgn, src, tile_x, tile_y, 
  1380.                                      tile_width, tile_height );
  1381.             
  1382.             begin_write_chunk( file, 'RGBA', 0L );
  1383.             x1 = (guint16)tile_x;
  1384.             y1 = (guint16)( drawable->height - ( tile_y + tile_height ) );
  1385.             x2 = (guint16)( tile_x + tile_width - 1 );
  1386.             y2 = (guint16)( drawable->height - tile_y - 1 );
  1387.             put_short( x1, file ); put_short( y1, file );
  1388.             put_short( x2, file ); put_short( y2, file );
  1389.             
  1390.             IFF_DEBUG_PRINT( "Writing - x1: %hu  ", x1 );
  1391.             IFF_DEBUG_PRINT( "y1: %hu  ", y1 );
  1392.             IFF_DEBUG_PRINT( "x2: %hu  ", x2 );
  1393.             IFF_DEBUG_PRINT( "y2: %hu\n", y2 );
  1394.             
  1395.             compress_tile_rle( file, src, tile_width, tile_height, depth );
  1396.             
  1397.             end_write_chunk( file ); /* RGBA */
  1398.         }
  1399.         gimp_progress_update ((double)(tiles_in_y - row) / (double)tiles_in_y);
  1400.     } 
  1401.     
  1402.     free( src );
  1403.     end_write_chunk( file ); /* FOR4 - TBMP */
  1404.  
  1405.     end_write_chunk( file ); /* FOR4 - CIMG */
  1406.     
  1407.     fclose( file );
  1408.     
  1409. #ifdef IFF_DEBUG    
  1410.     fclose(debugFile);
  1411. #endif
  1412.     
  1413.     return (1);
  1414. }
  1415.