home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / gfli / gfli.c < prev   
Encoding:
C/C++ Source or Header  |  2000-08-24  |  23.7 KB  |  938 lines

  1. /*
  2.  * GFLI 1.3
  3.  *
  4.  * A gimp plug-in to read and write FLI and FLC movies.
  5.  *
  6.  * Copyright (C) 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  *
  22.  * This is a first loader for FLI and FLC movies. It uses as the same method as
  23.  * the gif plug-in to store the animation (i.e. 1 layer/frame).
  24.  * 
  25.  * Current disadvantages:
  26.  * - Generates A LOT OF warnings.
  27.  * - Consumes a lot of memory (See wish-list: use the original data or 
  28.  *   compression).
  29.  * - doesn't support palette changes between two frames.
  30.  *
  31.  * Wish-List:
  32.  * - I'd like to have a different format for storing animations, so I can use
  33.  *   Layers and Alpha-Channels for effects. An older version of 
  34.  *   this plug-in created one image per frame, and went real soon out of
  35.  *   memory.
  36.  * - I'd like a method that requests unmodified frames from the original
  37.  *   image, and stores modified without destroying the original file.
  38.  * - I'd like a way to store additional information about a image to it, for
  39.  *   example copyright stuff or a timecode.
  40.  * - I've thought about a small utility to mix MIDI events as custom chunks
  41.  *   between the FLI chunks. Anyone interested in implementing this ?
  42.  */
  43.  
  44. /*
  45.  * History:
  46.  * 1.0 first release
  47.  * 1.1 first support for FLI saving (BRUN and LC chunks)
  48.  * 1.2 support for load/save ranges, fixed SGI & SUN problems (I hope...), fixed FLC
  49.  * 1.3 made saving actually work, alpha channel is silently ignored; 
  50.        loading was broken too, fixed it  --Sven
  51.  */
  52.  
  53. #include <config.h>
  54.  
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58.  
  59. #include <gtk/gtk.h>
  60.  
  61. #include <libgimp/gimp.h>
  62. #include <libgimp/gimpui.h>
  63.  
  64. #include "fli.h"
  65.  
  66. #include "libgimp/stdplugins-intl.h"
  67.  
  68.  
  69. static void query (void);
  70. static void run   (gchar   *name,
  71.            gint     nparams,
  72.            GimpParam  *param,
  73.            gint    *nreturn_vals,
  74.            GimpParam **return_vals);
  75.  
  76. /* return the image-ID of the new image, or -1 in case of an error */
  77. static gint32 load_image      (gchar  *filename,
  78.                    gint32  from_frame,
  79.                    gint32  to_frame);
  80. static gint   load_dialog     (gchar  *name);
  81.  
  82. /* return TRUE or FALSE */
  83. static gint   save_image      (gchar  *filename,
  84.                    gint32  image_id,
  85.                    gint32  from_frame,
  86.                    gint32  to_frame);
  87. static gint   save_dialog     (gint32  image_id);
  88.  
  89. /* return TRUE or FALSE */
  90. static gint   get_info        (gchar  *filename,
  91.                    gint32 *width,
  92.                    gint32 *height,
  93.                    gint32 *frames);
  94.  
  95. /*
  96.  * GIMP interface
  97.  */
  98. GimpPlugInInfo PLUG_IN_INFO =
  99. {
  100.   NULL,  /* init_proc  */
  101.   NULL,  /* quit_proc  */
  102.   query, /* query_proc */
  103.   run,   /* run_proc   */
  104. };
  105.  
  106. GimpParamDef load_args[] =
  107. {
  108.   { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  109.   { GIMP_PDB_STRING, "filename", "The name of the file to load" },
  110.   { GIMP_PDB_STRING, "raw_filename", "The name entered" },
  111.   { GIMP_PDB_INT32, "from_frame", "Load beginning from this frame" },
  112.   { GIMP_PDB_INT32, "to_frame", "End loading with this frame" },
  113. };
  114. GimpParamDef load_return_vals[] =
  115. {
  116.   { GIMP_PDB_IMAGE, "image", "Output image" },
  117. };
  118. gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  119. gint nload_return_vals = (sizeof (load_return_vals) /
  120.               sizeof (load_return_vals[0]));
  121.  
  122. GimpParamDef save_args[] =
  123. {
  124.   { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  125.   { GIMP_PDB_IMAGE, "image", "Input image" },
  126.   { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" },
  127.   { GIMP_PDB_STRING, "filename", "The name of the file to save" },
  128.   { GIMP_PDB_STRING, "raw_filename", "The name entered" },
  129.   { GIMP_PDB_INT32, "from_frame", "Save beginning from this frame" },
  130.   { GIMP_PDB_INT32, "to_frame", "End saving with this frame" },
  131. };
  132. gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  133.  
  134. GimpParamDef info_args[] =
  135. {
  136.   { GIMP_PDB_STRING, "filename", "The name of the file to get info" },
  137. };
  138.  
  139. GimpParamDef info_return_vals[] =
  140. {
  141.   { GIMP_PDB_INT32, "width", "Width of one frame" },
  142.   { GIMP_PDB_INT32, "height", "Height of one frame" },
  143.   { GIMP_PDB_INT32, "frames", "Number of Frames" },
  144. };
  145. gint ninfo_args = sizeof (info_args) / sizeof (info_args[0]);
  146. gint ninfo_return_vals = (sizeof (info_return_vals) /
  147.               sizeof (info_return_vals[0]));
  148.  
  149.  
  150. static gint32 from_frame;
  151. static gint32 to_frame;
  152.  
  153. MAIN ()
  154.  
  155. static void
  156. query (void)
  157. {
  158.   /*
  159.    * Load/save procedures
  160.    */
  161.   gimp_install_procedure ("file_fli_load",
  162.               "load FLI-movies",
  163.               "This is an experimantal plug-in to handle FLI movies",
  164.               "Jens Ch. Restemeier",
  165.               "Jens Ch. Restemeier",
  166.               "1997",
  167.               "<Load>/FLI",
  168.               NULL,
  169.               GIMP_PLUGIN,
  170.               nload_args - 2, nload_return_vals,
  171.               load_args, load_return_vals);
  172.  
  173.   gimp_register_magic_load_handler ("file_fli_load",
  174.                     "fli",
  175.                     "",
  176.                     "");
  177.   
  178.   gimp_install_procedure ("file_fli_save",
  179.               "save FLI-movies",
  180.               "This is an experimantal plug-in to handle FLI movies",
  181.               "Jens Ch. Restemeier",
  182.               "Jens Ch. Restemeier",
  183.               "1997",
  184.               "<Save>/FLI",
  185.               "INDEXED,GRAY",
  186.               GIMP_PLUGIN,
  187.               nsave_args, 0,
  188.               save_args, NULL);
  189.  
  190.   gimp_register_save_handler ("file_fli_save",
  191.                   "fli",
  192.                   "");
  193.  
  194.  
  195.   /*
  196.    * Utility functions:
  197.    */
  198.   gimp_install_procedure ("file_fli_info",
  199.               "Get info about a Fli movie",
  200.               "This is a experimantal plug-in to handle FLI movies",
  201.               "Jens Ch. Restemeier",
  202.               "Jens Ch. Restemeier",
  203.               "1997",
  204.               NULL,
  205.               NULL,
  206.               GIMP_EXTENSION,
  207.               ninfo_args, ninfo_return_vals,
  208.               info_args, info_return_vals);
  209. }
  210.  
  211. GimpParam values[5];
  212.  
  213. static void
  214. run (gchar   *name,
  215.      gint     nparams,
  216.      GimpParam  *param,
  217.      gint    *nreturn_vals,
  218.      GimpParam **return_vals)
  219. {
  220.   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  221.   GimpRunModeType run_mode;
  222.   gint32       pc;
  223.   gint32       image_ID;
  224.   gint32       drawable_ID;
  225.   gint32       orig_image_ID;
  226.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  227.  
  228.   run_mode = param[0].data.d_int32;
  229.  
  230.   *nreturn_vals = 1;
  231.   *return_vals  = values;
  232.   values[0].type          = GIMP_PDB_STATUS;
  233.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  234.  
  235.   if (strcmp (name, "file_fli_load") == 0)
  236.     {
  237.       INIT_I18N_UI ();
  238.  
  239.       switch (run_mode)
  240.     {
  241.     case GIMP_RUN_NONINTERACTIVE:
  242.       /*
  243.        * check for valid parameters: 
  244.        * (Or can I trust GIMP ?)
  245.        */
  246.       if ((nparams < nload_args - 2) || (nload_args < nparams))
  247.         {
  248.           status = GIMP_PDB_CALLING_ERROR;
  249.           break;
  250.         }
  251.       for (pc = 0; pc < nload_args - 2; pc++)
  252.         {
  253.           if (load_args[pc].type != param[pc].type)
  254.         {
  255.           status = GIMP_PDB_CALLING_ERROR;
  256.           break;
  257.         }
  258.         }
  259.       for (pc = nload_args - 2; pc < nparams; pc++)
  260.         {
  261.           if (load_args[pc].type != param[pc].type)
  262.         {
  263.           status = GIMP_PDB_CALLING_ERROR;
  264.           break;
  265.         }
  266.         }
  267.  
  268.       to_frame   = (nparams < nload_args - 1) ? 1 : param[3].data.d_int32;
  269.       from_frame = (nparams < nload_args) ? -1 : param[4].data.d_int32;
  270.       image_ID = load_image (param[1].data.d_string,
  271.                  from_frame, to_frame);
  272.  
  273.       if (image_ID != -1)
  274.         {
  275.           *nreturn_vals = 2;
  276.           values[1].type         = GIMP_PDB_IMAGE;
  277.           values[1].data.d_image = image_ID;
  278.         }
  279.       else
  280.         status = GIMP_PDB_EXECUTION_ERROR;
  281.       break;
  282.  
  283.     case GIMP_RUN_INTERACTIVE:
  284.       if (load_dialog (param[1].data.d_string))
  285.         {
  286.           image_ID = load_image (param[1].data.d_string,
  287.                      from_frame, to_frame);
  288.  
  289.           if (image_ID != -1)
  290.         {
  291.           *nreturn_vals = 2;
  292.           values[1].type         = GIMP_PDB_IMAGE;
  293.           values[1].data.d_image = image_ID;
  294.         }
  295.           else
  296.         status = GIMP_PDB_EXECUTION_ERROR;
  297.         }
  298.       else
  299.         status = GIMP_PDB_CANCEL;
  300.       break;
  301.  
  302.     case GIMP_RUN_WITH_LAST_VALS:
  303.       status = GIMP_PDB_CALLING_ERROR;
  304.       break;
  305.     }
  306.     }  
  307.   else if (strcmp (name, "file_fli_save") == 0)
  308.     {
  309.       INIT_I18N_UI ();
  310.  
  311.       image_ID    = orig_image_ID = param[1].data.d_int32;
  312.       drawable_ID = param[2].data.d_int32;
  313.  
  314.       switch (run_mode)
  315.     {
  316.     case GIMP_RUN_NONINTERACTIVE:
  317.       if (nparams!=nsave_args)
  318.         {
  319.           status = GIMP_PDB_CALLING_ERROR;
  320.           break;
  321.         }
  322.       for (pc = 0; pc < nsave_args; pc++)
  323.         {
  324.           if (save_args[pc].type!=param[pc].type)
  325.         {
  326.           status = GIMP_PDB_CALLING_ERROR;
  327.           break;
  328.         }
  329.         }
  330.       if (! save_image (param[3].data.d_string, image_ID,
  331.                 param[5].data.d_int32,
  332.                 param[6].data.d_int32))
  333.         status = GIMP_PDB_EXECUTION_ERROR;
  334.       break;
  335.  
  336.     case GIMP_RUN_INTERACTIVE:
  337.     case GIMP_RUN_WITH_LAST_VALS:
  338.       gimp_ui_init ("gfli", FALSE);
  339.       export = gimp_export_image (&image_ID, &drawable_ID, "FLI", 
  340.                       (GIMP_EXPORT_CAN_HANDLE_INDEXED |
  341.                        GIMP_EXPORT_CAN_HANDLE_GRAY | 
  342.                        GIMP_EXPORT_CAN_HANDLE_ALPHA  |
  343.                        GIMP_EXPORT_CAN_HANDLE_LAYERS));
  344.       if (export == GIMP_EXPORT_CANCEL)
  345.         {
  346.           values[0].data.d_status = GIMP_PDB_CANCEL;
  347.           return;
  348.         }
  349.  
  350.       if (save_dialog (param[1].data.d_image))
  351.         {
  352.           if (! save_image (param[3].data.d_string, image_ID, from_frame, to_frame))
  353.         status = GIMP_PDB_EXECUTION_ERROR;
  354.         }
  355.       else
  356.         status = GIMP_PDB_CANCEL;
  357.       break;
  358.     }
  359.  
  360.       if (export == GIMP_EXPORT_EXPORT)
  361.     gimp_image_delete (image_ID);
  362.     }
  363.   else if (strcmp (name, "file_fli_info") == 0)
  364.     {
  365.       gint32 width, height, frames;
  366.  
  367.       INIT_I18N_UI ();
  368.  
  369.       /*
  370.        * check for valid parameters;
  371.        */
  372.       if (nparams != ninfo_args)
  373.     status = GIMP_PDB_CALLING_ERROR;
  374.  
  375.       if (status == GIMP_PDB_SUCCESS)
  376.     {
  377.       for (pc = 0; pc < nsave_args; pc++)
  378.         {
  379.           if (info_args[pc].type != param[pc].type)
  380.         {
  381.           status = GIMP_PDB_CALLING_ERROR;
  382.           break;
  383.         }
  384.         }
  385.     }
  386.  
  387.       if (status == GIMP_PDB_SUCCESS)
  388.     {
  389.       if (get_info (param[0].data.d_string, &width, &height, &frames))
  390.         {
  391.           *nreturn_vals = 4;
  392.           values[1].type = GIMP_PDB_INT32;
  393.           values[1].data.d_int32 = width;
  394.           values[2].type = GIMP_PDB_INT32;
  395.           values[2].data.d_int32 = height;
  396.           values[3].type = GIMP_PDB_INT32;
  397.           values[3].data.d_int32 = frames;
  398.         }
  399.       else
  400.         status = GIMP_PDB_EXECUTION_ERROR;
  401.     }
  402.     }
  403.   else
  404.     status = GIMP_PDB_CALLING_ERROR;
  405.  
  406.   values[0].data.d_status = status;
  407. }
  408.  
  409. /*
  410.  * Open FLI animation and return header-info
  411.  */
  412. gint
  413. get_info (gchar  *filename,
  414.       gint32 *width,
  415.       gint32 *height,
  416.       gint32 *frames)
  417. {
  418.   FILE *file;
  419.   s_fli_header fli_header;
  420.  
  421.   *width = 0; *height = 0; *frames = 0;
  422.  
  423.   file = fopen (filename ,"rb");
  424.  
  425.   if (!file)
  426.     {
  427.       g_message (_("FLI: Can't open \"%s\""), filename);
  428.       return FALSE;
  429.     }
  430.   fli_read_header (file, &fli_header);
  431.   fclose (file);
  432.   *width = fli_header.width;
  433.   *height = fli_header.height;
  434.   *frames = fli_header.frames;
  435.  
  436.   return TRUE;
  437. }
  438.  
  439. /*
  440.  * load fli animation and store as framestack
  441.  */
  442. gint32
  443. load_image (gchar  *filename,
  444.         gint32  from_frame,
  445.         gint32  to_frame)
  446. {
  447.   FILE *file;
  448.   gchar *name_buf;
  449.   GimpDrawable *drawable;
  450.   gint32 image_id, layer_ID;
  451.       
  452.   guchar *fb, *ofb, *fb_x;
  453.   guchar cm[768], ocm[768];
  454.   GimpPixelRgn pixel_rgn;
  455.   s_fli_header fli_header;
  456.  
  457.   gint cnt;
  458.  
  459.   name_buf = g_strdup_printf (_("Loading %s:"), filename);
  460.   gimp_progress_init (name_buf);
  461.   g_free (name_buf);
  462.  
  463.   file = fopen (filename ,"rb");
  464.   if (!file)
  465.     {
  466.       g_message (_("FLI: Can't open \"%s\""), filename);
  467.       return -1;
  468.     }
  469.   fli_read_header (file, &fli_header);
  470.   if (fli_header.magic == NO_HEADER)
  471.     return -1;
  472.   else
  473.     fseek (file, 128, SEEK_SET);
  474.  
  475.   /*
  476.    * Fix parameters
  477.    */
  478.   if ((from_frame==-1) && (to_frame==-1))
  479.     {
  480.       /* to make scripting easier: */
  481.       from_frame=1; to_frame=fli_header.frames;
  482.     }
  483.   if (to_frame<from_frame)
  484.     {
  485.       to_frame = fli_header.frames;
  486.     }
  487.   if (from_frame < 1)
  488.     {
  489.       from_frame = 1;
  490.     }
  491.   if (to_frame < 1)
  492.     {
  493.       /* nothing to do ... */
  494.       return -1;
  495.     }
  496.   if (from_frame >= fli_header.frames)
  497.     {
  498.       /* nothing to do ... */
  499.       return -1;
  500.     }
  501.   if (to_frame>fli_header.frames)
  502.     {
  503.       to_frame = fli_header.frames;
  504.     }
  505.  
  506.   image_id = gimp_image_new (fli_header.width, fli_header.height, GIMP_INDEXED);
  507.   gimp_image_set_filename (image_id, filename);
  508.  
  509.   fb = g_malloc (fli_header.width * fli_header.height);
  510.   ofb = g_malloc (fli_header.width * fli_header.height);
  511.  
  512.   /*
  513.    * Skip to the beginning of requested frames: 
  514.    */
  515.   for (cnt = 1; cnt < from_frame; cnt++)
  516.     {
  517.       fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
  518.       memcpy (ocm, cm, 768);
  519.       fb_x = fb; fb = ofb; ofb = fb_x;
  520.     }
  521.   /*
  522.    * Load range
  523.    */
  524.   for (cnt = from_frame; cnt <= to_frame; cnt++)
  525.     {
  526.       name_buf = g_strdup_printf (_("Frame (%i)"), cnt); 
  527.       layer_ID = gimp_layer_new (image_id, name_buf,
  528.                  fli_header.width, fli_header.height,
  529.                  GIMP_INDEXED_IMAGE, 100, GIMP_NORMAL_MODE);
  530.       g_free (name_buf);
  531.  
  532.       drawable = gimp_drawable_get (layer_ID);
  533.  
  534.       fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
  535.  
  536.       gimp_pixel_rgn_init (&pixel_rgn, drawable,
  537.                0, 0, fli_header.width, fli_header.height,
  538.                TRUE, FALSE);
  539.       gimp_pixel_rgn_set_rect (&pixel_rgn, fb,
  540.                    0, 0, fli_header.width, fli_header.height);
  541.  
  542.       gimp_drawable_flush (drawable);
  543.       gimp_drawable_detach (drawable);
  544.  
  545.       if (cnt > 0)
  546.     gimp_layer_add_alpha (layer_ID);
  547.  
  548.       gimp_image_add_layer (image_id, layer_ID, 0);
  549.  
  550.       if (cnt < to_frame)
  551.     {
  552.       memcpy (ocm, cm, 768);
  553.       fb_x = fb; fb = ofb; ofb = fb_x;
  554.     }
  555.       
  556.       gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
  557.     }
  558.  
  559.   gimp_image_set_cmap (image_id, cm, 256);
  560.  
  561.   fclose (file);
  562.  
  563.   g_free (fb);
  564.   g_free (ofb);
  565.  
  566.   return image_id;
  567. }
  568.  
  569.  
  570. #define MAXDIFF 195075    /*  3 * SQR (255) + 1  */
  571.  
  572. /*
  573.  * get framestack and store as fli animation
  574.  * (some code was taken from the GIF plugin.)
  575.  */ 
  576. gint
  577. save_image (gchar  *filename,
  578.         gint32  image_id,
  579.         gint32  from_frame,
  580.         gint32  to_frame)
  581. {
  582.   FILE *file;
  583.   gchar *name_buf;
  584.   GimpDrawable *drawable;
  585.   gint32 *framelist;
  586.   gint nframes;
  587.   gint colors, i;
  588.   guchar *cmap;
  589.   guchar bg;
  590.   guchar red, green, blue;
  591.   gint diff, sum, max;
  592.   gint offset_x, offset_y, xc, yc, xx, yy;
  593.   guint rows, cols, bytes;
  594.   guchar *src_row;
  595.   guchar *fb, *ofb;
  596.   guchar cm[768];
  597.   GimpPixelRgn pixel_rgn;
  598.   s_fli_header fli_header;
  599.  
  600.   gint cnt;
  601.  
  602.   framelist = gimp_image_get_layers (image_id, &nframes);
  603.     
  604.   if ((from_frame == -1) && (to_frame == -1))
  605.     {
  606.       /* to make scripting easier: */
  607.       from_frame = 0; to_frame = nframes;
  608.     }
  609.   if (to_frame < from_frame)
  610.     {
  611.       to_frame = nframes;
  612.     }
  613.   if (from_frame < 1)
  614.     {
  615.       from_frame = 1;
  616.     }
  617.   if (to_frame < 1)
  618.     {
  619.       /* nothing to do ... */
  620.       return FALSE;
  621.     }
  622.   if (from_frame > nframes)
  623.     {
  624.       /* nothing to do ... */
  625.       return FALSE;
  626.     }
  627.   if (to_frame > nframes)
  628.     {
  629.       to_frame = nframes;
  630.     }
  631.  
  632.   gimp_palette_get_background (&red, &green, &blue);
  633.  
  634.   switch (gimp_image_base_type (image_id))
  635.     {
  636.     case GIMP_GRAY:
  637.       /* build grayscale palette */
  638.       for (i = 0; i < 256; i++)
  639.     {
  640.       cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
  641.     }
  642.       bg = INTENSITY (red, green, blue);
  643.       break;
  644.  
  645.     case GIMP_INDEXED:
  646.       max = MAXDIFF;
  647.       bg = 0;
  648.       cmap = gimp_image_get_cmap (image_id, &colors);
  649.       for (i = 0; i < MIN (colors, 256); i++)
  650.     {
  651.       cm[i*3+0] = cmap[i*3+0];
  652.       cm[i*3+1] = cmap[i*3+1];
  653.       cm[i*3+2] = cmap[i*3+2];
  654.       
  655.       diff = red - cm[i*3+0];
  656.       sum = SQR (diff);
  657.       diff = green - cm[i*3+1];
  658.       sum +=  SQR (diff);
  659.       diff = blue - cm[i*3+2];
  660.       sum += SQR (diff);
  661.  
  662.       if (sum < max)
  663.         {
  664.           bg = i;
  665.           max = sum;
  666.         }
  667.     }
  668.       for (i = colors; i < 256; i++)
  669.     {
  670.       cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
  671.     }
  672.       break;
  673.       
  674.     default:
  675.       g_message (_("FLI: Sorry, I can save only INDEXED and GRAY images."));
  676.       return FALSE;
  677.     }
  678.  
  679.   name_buf = g_strdup_printf (_("Saving %s:"), filename);
  680.   gimp_progress_init (name_buf);
  681.   g_free (name_buf);
  682.  
  683.   /*
  684.    * First build the fli header.
  685.    */
  686.   fli_header.filesize = 0;    /* will be fixed when writing the header */
  687.   fli_header.frames   = 0;     /* will be fixed during the write */
  688.   fli_header.width    = gimp_image_width (image_id);
  689.   fli_header.height   = gimp_image_height (image_id);
  690.  
  691.   if ((fli_header.width == 320) && (fli_header.height == 200))
  692.     {
  693.       fli_header.magic = HEADER_FLI;
  694.     }
  695.   else
  696.     {
  697.       fli_header.magic = HEADER_FLC;
  698.     }
  699.   fli_header.depth    = 8;  /* I've never seen a depth != 8 */
  700.   fli_header.flags    = 3;
  701.   fli_header.speed    = 1000 / 25;
  702.   fli_header.created  = 0;  /* program ID. not neccessary... */
  703.   fli_header.updated  = 0;  /* date in MS-DOS format. ignore...*/
  704.   fli_header.aspect_x = 1;  /* aspect ratio. Will be added as soon.. */
  705.   fli_header.aspect_y = 1;  /* ... as GIMP supports it. */
  706.   fli_header.oframe1  = fli_header.oframe2 = 0; /* will be fixed during the write */
  707.  
  708.   file = fopen (filename ,"wb");
  709.   if (!file)
  710.     {
  711.       g_message (_("FLI: Can't open \"%s\""), filename);
  712.       return FALSE;
  713.     }
  714.   fseek (file, 128, SEEK_SET);
  715.  
  716.   fb = g_malloc (fli_header.width * fli_header.height);
  717.   ofb = g_malloc (fli_header.width * fli_header.height);
  718.  
  719.   /* initialize with bg color */
  720.   memset (fb, bg, fli_header.width * fli_header.height);
  721.  
  722.   /*
  723.    * Now write all frames
  724.    */
  725.   for (cnt = from_frame; cnt <= to_frame; cnt++)
  726.     {
  727.      /* get layer data from GIMP */
  728.       drawable = gimp_drawable_get (framelist[nframes-cnt]);
  729.       gimp_drawable_offsets (framelist[nframes-cnt], &offset_x, &offset_y);
  730.       cols = drawable->width;
  731.       rows = drawable->height;
  732.       bytes = drawable->bpp;
  733.       gimp_pixel_rgn_init (&pixel_rgn, drawable,
  734.                0, 0, cols, rows,
  735.                FALSE, FALSE);
  736.       src_row = g_malloc (cols * bytes);
  737.  
  738.       /* now paste it into the framebuffer, with the neccessary offset */
  739.       for (yc = 0, yy = offset_y; yc < rows; yc++, yy++)
  740.     {
  741.       if (yy >= 0 && yy < fli_header.height)
  742.         {
  743.           gimp_pixel_rgn_get_row (&pixel_rgn, src_row, 0, yc, cols);
  744.           
  745.           for (xc = 0, xx = offset_x; xc < cols; xc++, xx++)
  746.         {
  747.           if (xx >= 0 && xx < fli_header.width)
  748.             fb[yy * fli_header.width + xx] = src_row[xc * bytes];
  749.         }
  750.         }
  751.     }
  752.  
  753.       g_free (src_row);
  754.  
  755.       /* save the frame */
  756.       if (cnt > from_frame)
  757.     {
  758.       /* save frame, allow all codecs */
  759.       fli_write_frame (file, &fli_header, ofb, cm, fb, cm, W_ALL);
  760.     }
  761.       else
  762.     {
  763.       /* save first frame, no delta information, allow all codecs */
  764.       fli_write_frame (file, &fli_header, NULL, NULL, fb, cm, W_ALL);
  765.     }
  766.  
  767.       if (cnt < to_frame)
  768.     memcpy (ofb, fb, fli_header.width * fli_header.height);
  769.  
  770.       gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
  771.      }
  772.  
  773.   /*
  774.    * finish fli
  775.    */
  776.   fli_write_header (file, &fli_header);
  777.   fclose (file);
  778.  
  779.   g_free (fb);
  780.   g_free (ofb);
  781.   g_free (framelist);
  782.  
  783.   return TRUE;
  784. }
  785.  
  786. /*
  787.  * Dialogs for interactive usage
  788.  */
  789. gint result = FALSE;
  790.  
  791. static void
  792. cb_ok (GtkWidget *widget,
  793.        gpointer   data)
  794. {
  795.   result = TRUE;
  796.  
  797.   gtk_widget_destroy (GTK_WIDGET (data));
  798. }
  799.  
  800. gint
  801. load_dialog (gchar *name)
  802. {
  803.   GtkWidget *dialog;
  804.   GtkWidget *table;
  805.   GtkWidget *spinbutton;
  806.   GtkObject *adj;
  807.  
  808.   gint32 width, height, nframes;
  809.   get_info (name, &width, &height, &nframes);
  810.  
  811.   from_frame = 1;
  812.   to_frame   = nframes;
  813.  
  814.   gimp_ui_init ("gfli", FALSE);
  815.  
  816.   dialog = gimp_dialog_new (_("GFLI 1.3 - Load framestack"), "gfli",
  817.                 gimp_standard_help_func, "filters/gfli.html",
  818.                 GTK_WIN_POS_MOUSE,
  819.                 FALSE, TRUE, FALSE,
  820.  
  821.                 _("OK"), cb_ok,
  822.                 NULL, NULL, NULL, TRUE, FALSE,
  823.                 _("Cancel"), gtk_widget_destroy,
  824.                 NULL, 1, NULL, FALSE, TRUE,
  825.  
  826.                 NULL);
  827.  
  828.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  829.               GTK_SIGNAL_FUNC (gtk_main_quit),
  830.               NULL);
  831.  
  832.   table = gtk_table_new (2, 2, FALSE);
  833.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  834.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  835.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  836.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table,
  837.              FALSE, FALSE, 0);
  838.   gtk_widget_show (table);
  839.  
  840.   /*
  841.    * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
  842.    * But for now you can set a start- and a end-frame:
  843.    */
  844.   spinbutton = gimp_spin_button_new (&adj,
  845.                      from_frame, 1, nframes, 1, 10, 0, 1, 0);
  846.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  847.                  _("From:"), 1.0, 0.5,
  848.                  spinbutton, 1, TRUE);
  849.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  850.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  851.               &from_frame);
  852.  
  853.   spinbutton = gimp_spin_button_new (&adj,
  854.                      to_frame, 1, nframes, 1, 10, 0, 1, 0);
  855.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  856.                  _("To:"), 1.0, 0.5,
  857.                  spinbutton, 1, TRUE);
  858.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  859.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  860.               &to_frame);
  861.  
  862.   gtk_widget_show (dialog);
  863.  
  864.   gtk_main ();
  865.   gdk_flush ();
  866.  
  867.   return result;
  868. }
  869.  
  870. gint
  871. save_dialog (gint32 image_id)
  872. {
  873.   GtkWidget *dialog;
  874.   GtkWidget *table;
  875.   GtkWidget *spinbutton;
  876.   GtkObject *adj;
  877.  
  878.   gint nframes;
  879.  
  880.   g_free (gimp_image_get_layers (image_id, &nframes));
  881.  
  882.   from_frame = 1;
  883.   to_frame   = nframes;
  884.  
  885.   dialog = gimp_dialog_new (_("GFLI 1.3 - Save framestack"), "gfli",
  886.                 gimp_standard_help_func, "filters/gfli.html",
  887.                 GTK_WIN_POS_MOUSE,
  888.                 FALSE, TRUE, FALSE,
  889.  
  890.                 _("OK"), cb_ok,
  891.                 NULL, NULL, NULL, TRUE, FALSE,
  892.                 _("Cancel"), gtk_widget_destroy,
  893.                 NULL, 1, NULL, FALSE, TRUE,
  894.  
  895.                 NULL);
  896.  
  897.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  898.               GTK_SIGNAL_FUNC (gtk_main_quit),
  899.               NULL);
  900.  
  901.   table = gtk_table_new (2, 2, FALSE);
  902.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  903.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  904.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  905.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table,
  906.              FALSE, FALSE, 0);
  907.   gtk_widget_show (table);
  908.  
  909.   /*
  910.    * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
  911.    * But for now you can set a start- and a end-frame:
  912.    */
  913.   spinbutton = gimp_spin_button_new (&adj,
  914.                      from_frame, 1, nframes, 1, 10, 0, 1, 0);
  915.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  916.                  _("From:"), 1.0, 0.5,
  917.                  spinbutton, 1, TRUE);
  918.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  919.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  920.               &from_frame);
  921.  
  922.   spinbutton = gimp_spin_button_new (&adj,
  923.                      to_frame, 1, nframes, 1, 10, 0, 1, 0);
  924.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  925.                  _("To:"), 1.0, 0.5,
  926.                  spinbutton, 1, TRUE);
  927.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  928.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  929.               &to_frame);
  930.  
  931.   gtk_widget_show (dialog);
  932.  
  933.   gtk_main ();
  934.   gdk_flush ();
  935.  
  936.   return result;
  937. }
  938.