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

  1. /* cel.c -- KISS CEL file format plug-in for The GIMP
  2.  * (copyright) 1997,1998 Nick Lamb (njl195@zepler.org.uk)
  3.  *
  4.  * Skeleton cloned from Michael Sweet's PNG plug-in. KISS format courtesy
  5.  * of the KISS/GS documentation. Problem reports to the above address
  6.  */
  7.  
  8. /* History:
  9.  * 0.1    Very limited functionality (modern 4bit only)
  10.  * 0.2  Default palette (nice yellows) is automatically used
  11.  * 0.3  Support for the older (pre KISS/GS) cell format
  12.  * 0.4  First support for saving images
  13.  * 0.5  Show copyright date, not version number, thanks to DbBrowser
  14.  * 0.6  File dialogs, palette handling, better magic behaviour
  15.  * 0.7  Handle interactivity settings, tidy up
  16.  * 1.0  Fixed for GIMP 0.99.27 running on GTK+ 1.0.0, and released
  17.  * 1.1  Oops, #include unistd.h, thanks Tamito Kajiyama
  18.  * 1.2  Changed email address, tidied up
  19.  * 1.3  Added g_message features, fixed Saving bugs...
  20.  * 1.4  Offsets work (needed them for a nice example set)
  21.  *
  22.  * Possible future additions:
  23.  *  +   Save (perhaps optionally?) the palette in a KCF
  24.  */
  25. #include "config.h"
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #ifdef HAVE_UNISTD_H
  31. #include <unistd.h>
  32. #endif
  33.  
  34. #include <gtk/gtk.h>
  35.  
  36. #include <libgimp/gimp.h>
  37. #include <libgimp/gimpui.h>
  38.  
  39. #include "libgimp/stdplugins-intl.h"
  40.  
  41. static void query (void);
  42. static void run   (gchar   *name,
  43.            gint     nparams,
  44.            GimpParam  *param,
  45.            gint    *nreturn_vals,
  46.            GimpParam **return_vals);
  47.  
  48. static gint   load_palette   (FILE   *fp,
  49.                   guchar  palette[]);
  50. static gint32 load_image     (gchar  *file,
  51.                   gchar  *brief);
  52. static gint   save_image     (gchar  *file,
  53.                   gchar  *brief,
  54.                   gint32  image,
  55.                   gint32  layer);
  56. static void   palette_dialog (gchar  *title);
  57.  
  58. /* Globals... */
  59.  
  60. GimpPlugInInfo    PLUG_IN_INFO =
  61. {
  62.   NULL,  /* init_proc  */
  63.   NULL,  /* quit_proc  */
  64.   query, /* query_proc */
  65.   run,   /* run_proc   */
  66. };
  67.  
  68. static gchar  *palette_file = NULL;
  69. static size_t  data_length  = 0;
  70.  
  71. /* Let GIMP library handle initialisation (and inquisitive users) */
  72.  
  73. MAIN ()
  74.  
  75. /* GIMP queries plug-in for parameters etc. */
  76.  
  77. static void
  78. query (void)
  79. {
  80.   static GimpParamDef load_args[] =
  81.   {
  82.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  83.     { GIMP_PDB_STRING, "filename", "Filename to load image from" },
  84.     { GIMP_PDB_STRING, "raw_filename", "Name entered" },
  85.     { GIMP_PDB_STRING, "palette_filename", "Filename to load palette from" }
  86.   };
  87.   static GimpParamDef load_return_vals[] =
  88.   {
  89.     { GIMP_PDB_IMAGE, "image", "Output image" }
  90.   };
  91.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  92.   static gint nload_return_vals = (sizeof (load_return_vals) /
  93.                    sizeof (load_return_vals[0]));
  94.  
  95.   static GimpParamDef save_args[] =
  96.   {
  97.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  98.     { GIMP_PDB_IMAGE, "image", "Input image" },
  99.     { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
  100.     { GIMP_PDB_STRING, "filename", "Filename to save image to" },
  101.     { GIMP_PDB_STRING, "raw_filename", "Name entered" },
  102.     { GIMP_PDB_STRING, "palette_filename", "Filename to save palette to" },
  103.   };
  104.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  105.  
  106.   gimp_install_procedure ("file_cel_load",
  107.               "Loads files in KISS CEL file format",
  108.               "This plug-in loads individual KISS cell files.",
  109.               "Nick Lamb",
  110.               "Nick Lamb <njl195@zepler.org.uk>",
  111.               "May 1998",
  112.               "<Load>/CEL",
  113.               NULL,
  114.               GIMP_PLUGIN, 
  115.               nload_args, nload_return_vals,
  116.               load_args, load_return_vals);
  117.  
  118.   gimp_install_procedure ("file_cel_save",
  119.               "Saves files in KISS CEL file format",
  120.               "This plug-in saves individual KISS cell files.",
  121.               "Nick Lamb",
  122.               "Nick Lamb <njl195@zepler.org.uk>",
  123.               "May 1998",
  124.               "<Save>/CEL",
  125.               "INDEXEDA",
  126.               GIMP_PLUGIN,
  127.               nsave_args, 0,
  128.               save_args, NULL);
  129.  
  130.   gimp_register_magic_load_handler ("file_cel_load",
  131.                     "cel",
  132.                     "",
  133.                     "0,string,KiSS\\040");
  134.   gimp_register_save_handler       ("file_cel_save",
  135.                     "cel",
  136.                     "");
  137. }
  138.  
  139. static void
  140. run (gchar   *name,
  141.      gint     nparams,
  142.      GimpParam  *param,
  143.      gint    *nreturn_vals,
  144.      GimpParam **return_vals)
  145. {
  146.   static GimpParam    values[2]; /* Return values */
  147.   GimpRunModeType  run_mode;
  148.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  149.   gint32        image;
  150.  
  151.   run_mode = param[0].data.d_int32;
  152.  
  153.   /* Set up default return values */
  154.  
  155.   *nreturn_vals = 1;
  156.   *return_vals  = values;
  157.   values[0].type          = GIMP_PDB_STATUS;
  158.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  159.  
  160.   if (run_mode == GIMP_RUN_INTERACTIVE)
  161.     {
  162.       INIT_I18N_UI();
  163.     }
  164.   else
  165.     {
  166.       INIT_I18N();
  167.     }
  168.  
  169.   if (strcmp (name, "file_cel_load") == 0)
  170.     {
  171.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  172.     {
  173.       gimp_get_data ("file_cel_save:length", &data_length);
  174.       if (data_length > 0)
  175.         {
  176.           palette_file = (char *) g_malloc(data_length);
  177.           gimp_get_data ("file_cel_save:data", palette_file);
  178.         }
  179.       else
  180.         {
  181.           palette_file = g_strdup("*.kcf");
  182.           data_length = strlen(palette_file) + 1;
  183.         }
  184.     }
  185.  
  186.       if (run_mode == GIMP_RUN_NONINTERACTIVE)
  187.     {
  188.       palette_file = param[3].data.d_string;
  189.       data_length = strlen(palette_file) + 1;
  190.     }
  191.       else if (run_mode == GIMP_RUN_INTERACTIVE)
  192.     {
  193.       /* Let user choose KCF palette (cancel ignores) */
  194.       palette_dialog (_("Load KISS Palette"));
  195.       gimp_set_data ("file_cel_save:length", &data_length, sizeof (size_t));
  196.       gimp_set_data ("file_cel_save:data", palette_file, data_length);
  197.     }
  198.  
  199.       image = load_image (param[1].data.d_string, param[2].data.d_string);
  200.  
  201.       if (image != -1)
  202.     {
  203.       *nreturn_vals = 2;
  204.       values[1].type         = GIMP_PDB_IMAGE;
  205.       values[1].data.d_image = image;
  206.     }
  207.       else
  208.     {
  209.       status = GIMP_PDB_EXECUTION_ERROR;
  210.     }
  211.     }
  212.   else if (strcmp (name, "file_cel_save") == 0)
  213.     {
  214.       if (! save_image (param[3].data.d_string, param[4].data.d_string,
  215.             param[1].data.d_int32, param[2].data.d_int32))
  216.     {
  217.       status = GIMP_PDB_EXECUTION_ERROR;
  218.     }
  219.       else
  220.     {
  221.       gimp_set_data ("file_cel_save:length", &data_length, sizeof (size_t));
  222.       gimp_set_data ("file_cel_save:data", palette_file, data_length);
  223.     }
  224.     }
  225.   else
  226.     {
  227.       status = GIMP_PDB_CALLING_ERROR;
  228.     }
  229.  
  230.   values[0].data.d_status = status;
  231. }
  232.  
  233. /* Load CEL image into The GIMP */
  234.  
  235. static gint32
  236. load_image (gchar *file,
  237.         gchar *brief)
  238. {
  239.   FILE      *fp;            /* Read file pointer */
  240.   gchar     *progress;      /* Title for progress display */
  241.   guchar     header[32];    /* File header */
  242.   gint       height, width, /* Dimensions of image */
  243.              offx, offy,    /* Layer offets */
  244.              colours;       /* Number of colours */
  245.  
  246.   gint32     image,         /* Image */
  247.              layer;         /* Layer */
  248.   guchar    *palette,       /* 24 bit palette */
  249.             *buffer,        /* Temporary buffer */
  250.             *line;          /* Pixel data */
  251.   GimpDrawable *drawable;      /* Drawable for layer */
  252.   GimpPixelRgn  pixel_rgn;     /* Pixel region for layer */
  253.  
  254.   gint       i, j, k;       /* Counters */
  255.  
  256.  
  257.   /* Open the file for reading */
  258.   fp = fopen (file, "r");
  259.  
  260.   if (fp == NULL)
  261.     {
  262.       g_message (_("%s\nis not present or is unreadable"), file);
  263.       return -1;
  264.     }
  265.  
  266.   progress = g_strdup_printf (_("Loading %s:"), brief);
  267.   gimp_progress_init (progress);
  268.   g_free (progress);
  269.  
  270.   /* Get the image dimensions and create the image... */
  271.  
  272.   fread (header, 4, 1, fp);
  273.  
  274.   if (strncmp(header, "KiSS", 4))
  275.     {
  276.       colours= 16;
  277.       width= header[0] + (256 * header[1]);
  278.       height= header[2] + (256 * header[3]);
  279.       offx= 0;
  280.       offy= 0;
  281.     }
  282.   else
  283.     { /* New-style image file, read full header */
  284.       fread (header, 28, 1, fp);
  285.       colours = (1 << header[1]);
  286.       width = header[4] + (256 * header[5]);
  287.       height = header[6] + (256 * header[7]);
  288.       offx = header[8] + (256 * header[9]);
  289.       offy = header[10] + (256 * header[11]);
  290.     }
  291.  
  292.   image = gimp_image_new (width + offx, height + offy, GIMP_INDEXED);
  293.  
  294.   if (image == -1)
  295.     {
  296.       g_message (_("CEL Can't create a new image"));
  297.       return -1;
  298.     }
  299.  
  300.   gimp_image_set_filename (image, file);
  301.  
  302.   /* Create an indexed-alpha layer to hold the image... */
  303.  
  304.   layer = gimp_layer_new (image, _("Background"), width, height,
  305.               GIMP_INDEXEDA_IMAGE, 100, GIMP_NORMAL_MODE);
  306.   gimp_image_add_layer (image, layer, 0);
  307.   gimp_layer_set_offsets (layer, offx, offy);
  308.  
  309.   /* Get the drawable and set the pixel region for our load... */
  310.  
  311.   drawable = gimp_drawable_get (layer);
  312.  
  313.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width,
  314.                drawable->height, TRUE, FALSE);
  315.  
  316.   /* Read the image in and give it to the GIMP a line at a time */
  317.  
  318.   buffer = g_new (guchar, width);
  319.   line = g_new (guchar, (width+1) * 2);
  320.  
  321.   for (i = 0; i < height && !feof(fp); ++i)
  322.     {
  323.       switch (colours)
  324.     {
  325.     case 16:
  326.       fread (buffer, (width+1)/2, 1, fp);
  327.       for (j = 0, k = 0; j < width*2; j+= 4, ++k)
  328.         { 
  329.           if (buffer[k] / 16 == 0)
  330.         {
  331.           line[j]= 16;
  332.           line[j+1]= 0;
  333.         }
  334.           else
  335.         {
  336.           line[j]= (buffer[k] / 16) - 1;
  337.           line[j+1]= 255;
  338.         }
  339.           if (buffer[k] % 16 == 0)
  340.         {
  341.           line[j+2]= 16;
  342.           line[j+3]= 0;
  343.         }
  344.           else
  345.         {
  346.           line[j+2]= (buffer[k] % 16) - 1;
  347.           line[j+3]= 255;
  348.         }
  349.         }
  350.       break;
  351.  
  352.     case 256:
  353.       fread (buffer, width, 1, fp);
  354.       for (j = 0, k = 0; j < width*2; j+= 2, ++k)
  355.         {
  356.           if (buffer[k] == 0)
  357.         {
  358.           line[j]= 255;
  359.           line[j+1]= 0;
  360.         }
  361.           else
  362.         {
  363.           line[j]= buffer[k] - 1;
  364.           line[j+1]= 255;
  365.         }
  366.         }
  367.       break;
  368.  
  369.     default:
  370.       g_message (_("Unsupported number of colors (%d)"), colours);
  371.       return -1;
  372.     }
  373.     
  374.       gimp_pixel_rgn_set_rect (&pixel_rgn, line, 0, i, drawable->width, 1);
  375.       gimp_progress_update ((float) i / (float) height);
  376.     }
  377.  
  378.   /* Close image files, give back allocated memory */
  379.  
  380.   fclose (fp);
  381.   g_free (buffer);
  382.   g_free (line);
  383.  
  384.   /* Use palette from file or otherwise default grey palette */
  385.   palette = g_new (guchar, colours*3);
  386.  
  387.   /* Open the file for reading if user picked one */
  388.   if (palette_file == NULL)
  389.     {
  390.       fp = NULL;
  391.     }
  392.   else
  393.     {
  394.       fp = fopen (palette_file, "r");
  395.     }
  396.  
  397.   if (fp != NULL)
  398.     {
  399.       colours = load_palette (fp, palette);
  400.       fclose (fp);
  401.     }
  402.   else
  403.     {
  404.       for (i= 0; i < colours; ++i)
  405.     {
  406.       palette[i*3] = palette[i*3+1] = palette[i*3+2]= i * 256 / colours;
  407.     }
  408.     }
  409.  
  410.   gimp_image_set_cmap (image, palette + 3, colours - 1);
  411.  
  412.   /* Close palette file, give back allocated memory */
  413.  
  414.   g_free (palette);
  415.  
  416.   /* Now get everything redrawn and hand back the finished image */
  417.  
  418.   gimp_drawable_flush (drawable);
  419.   gimp_drawable_detach (drawable);
  420.  
  421.   return (image);
  422. }
  423.  
  424. static gint
  425. load_palette (FILE   *fp,
  426.           guchar  palette[])
  427. {
  428.   guchar    header[32];    /* File header */
  429.   guchar    buffer[2];
  430.   int        i, bpp, colours= 0;
  431.  
  432.   fread (header, 4, 1, fp);
  433.   if (!strncmp(header, "KiSS", 4))
  434.     {
  435.       fread (header+4, 28, 1, fp);
  436.       bpp = header[5];
  437.       colours = header[8] + header[9] * 256;
  438.       if (bpp == 12)
  439.     {
  440.       for (i= 0; i < colours; ++i)
  441.         {
  442.           fread (buffer, 1, 2, fp);
  443.           palette[i*3]= buffer[0] & 0xf0;
  444.           palette[i*3+1]= (buffer[1] & 0x0f) * 16;
  445.           palette[i*3+2]= (buffer[0] & 0x0f) * 16;
  446.         }
  447.     }
  448.       else
  449.     {
  450.       fread (palette, colours, 3, fp);
  451.     }
  452.     }
  453.   else
  454.     {
  455.       colours = 16; bpp = 12;
  456.       fseek (fp, 0, SEEK_SET);
  457.       for (i= 0; i < colours; ++i)
  458.     {
  459.       fread (buffer, 1, 2, fp);
  460.       palette[i*3] = buffer[0] & 0xf0;
  461.       palette[i*3+1] = (buffer[1] & 0x0f) * 16;
  462.       palette[i*3+2] = (buffer[0] & 0x0f) * 16;
  463.     }
  464.     }
  465.  
  466.   return colours;
  467. }
  468.  
  469. static gint
  470. save_image (gchar  *file,
  471.         gchar  *brief,
  472.         gint32  image,
  473.         gint32  layer)
  474. {
  475.   FILE*        fp;        /* Write file pointer */
  476.   char        *progress;    /* Title for progress display */
  477.   guchar    header[32];    /* File header */
  478.   gint        colours, type,    /* Number of colours, type of layer */
  479.           offx, offy;    /* Layer offsets */
  480.  
  481.   guchar    *buffer,    /* Temporary buffer */
  482.           *line;        /* Pixel data */
  483.   GimpDrawable    *drawable;    /* Drawable for layer */
  484.   GimpPixelRgn    pixel_rgn;    /* Pixel region for layer */
  485.  
  486.   int        i, j, k;    /* Counters */
  487.  
  488.   /* Check that this is an indexed image, fail otherwise */
  489.   type = gimp_drawable_type (layer);
  490.   if (type != GIMP_INDEXEDA_IMAGE)
  491.     {
  492.       g_message (_("Only an indexed-alpha image can be saved in CEL format"));
  493.       return FALSE;
  494.     }
  495.  
  496.   /* Find out how offset this layer was */
  497.   gimp_drawable_offsets (layer, &offx, &offy);
  498.  
  499.   drawable = gimp_drawable_get (layer);
  500.  
  501.   /* Open the file for writing */
  502.   fp = fopen (file, "w");
  503.  
  504.   if (fp == NULL)
  505.     {
  506.       g_message (_("CEL Couldn't write image to\n%s"), file);
  507.       return FALSE;
  508.     }
  509.  
  510.   progress = g_strdup_printf (_("Saving %s:"), brief);
  511.   gimp_progress_init (progress);
  512.   g_free (progress);
  513.  
  514.   /* Headers */
  515.   memset (header, 0, 32);
  516.   strcpy (header, "KiSS");
  517.   header[4]= 0x20;
  518.  
  519.   /* Work out whether to save as 8bit or 4bit */
  520.   g_free (gimp_image_get_cmap (image, &colours));
  521.   if (colours > 15)
  522.     {
  523.       header[5] = 8;
  524.     }
  525.   else
  526.     {
  527.       header[5] = 4;
  528.     }
  529.  
  530.   /* Fill in the blanks ... */
  531.   header[8]  = drawable->width % 256;
  532.   header[9]  = drawable->width / 256;
  533.   header[10] = drawable->height % 256;
  534.   header[11] = drawable->height / 256;
  535.   header[12] = offx % 256;
  536.   header[13] = offx / 256;
  537.   header[14] = offy % 256;
  538.   header[15] = offy / 256;
  539.   fwrite (header, 32, 1, fp);
  540.  
  541.   /* Arrange for memory etc. */
  542.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width,
  543.                drawable->height, TRUE, FALSE);
  544.   buffer = g_new (guchar, drawable->width);
  545.   line = g_new (guchar, (drawable->width+1) * 2);
  546.  
  547.   /* Get the image from the GIMP one line at a time and write it out */
  548.   for (i = 0; i < drawable->height; ++i)
  549.     {
  550.       gimp_pixel_rgn_get_rect (&pixel_rgn, line, 0, i, drawable->width, 1);
  551.       memset (buffer, 0, drawable->width);
  552.  
  553.       if (colours > 16)
  554.     {
  555.       for (j = 0, k = 0; j < drawable->width*2; j+= 2, ++k)
  556.         {
  557.           if (line[j+1] > 127)
  558.         {
  559.           buffer[k]= line[j] + 1;
  560.         }
  561.         }
  562.       fwrite (buffer, drawable->width, 1, fp);
  563.     }
  564.       else
  565.     {
  566.       for (j = 0, k = 0; j < drawable->width*2; j+= 4, ++k)
  567.         { 
  568.           buffer[k] = 0;
  569.           if (line[j+1] > 127)
  570.         {
  571.           buffer[k] += (line[j] + 1)<< 4;
  572.         }
  573.           if (line[j+3] > 127)
  574.         {
  575.           buffer[k] += (line[j+2] + 1);
  576.         }
  577.         }
  578.       fwrite (buffer, (drawable->width+1)/2, 1, fp);
  579.     }
  580.  
  581.       gimp_progress_update ((float) i / (float) drawable->height);
  582.     }
  583.  
  584.   /* Close files, give back allocated memory */
  585.   fclose (fp);
  586.   g_free (buffer);
  587.   g_free (line);
  588.  
  589.   return TRUE;
  590. }
  591.  
  592. static void
  593. palette_ok (GtkWidget  *widget,
  594.         GtkWidget **fs)
  595. {
  596.   g_free (palette_file);
  597.   palette_file =
  598.     g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
  599.   data_length = strlen (palette_file) + 1;
  600.  
  601.   gtk_widget_destroy (GTK_WIDGET (fs));
  602. }
  603.  
  604. static void
  605. palette_dialog (gchar *title)
  606. {
  607.   GtkWidget *dialog;
  608.  
  609.   gimp_ui_init ("CEL", FALSE);
  610.  
  611.   dialog = gtk_file_selection_new (title);
  612.   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
  613.   gtk_file_selection_set_filename (GTK_FILE_SELECTION (dialog), palette_file);
  614.  
  615.   gtk_signal_connect
  616.     (GTK_OBJECT (GTK_FILE_SELECTION (dialog)->ok_button), "clicked",
  617.      GTK_SIGNAL_FUNC (palette_ok),
  618.      dialog);
  619.   gtk_signal_connect_object
  620.     (GTK_OBJECT (GTK_FILE_SELECTION (dialog)->cancel_button), "clicked",
  621.      GTK_SIGNAL_FUNC (gtk_widget_destroy),
  622.      GTK_OBJECT (dialog));
  623.  
  624.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  625.               GTK_SIGNAL_FUNC (gtk_main_quit),
  626.               NULL);
  627.  
  628.   gimp_help_connect_help_accel (dialog, gimp_standard_help_func,
  629.                 "filters/cel.html");
  630.  
  631.   gtk_widget_show (dialog);
  632.  
  633.   gtk_main ();
  634.   gdk_flush ();
  635. }
  636.