/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "dumpToGif.h"

#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include <visu_tools.h>
#include <opengl.h>

#include <glib.h>
/* #include <unistd.h> */
#include <time.h>

/* une grande partie des modules utilisés ici consiste en
   une modification (L. Billard 1997 - 2001) de modules tirés du 
   'package' ImageMagick de cristy@dupont.com.
   
   Le but est de réduire le nombre de couleurs à 256 au plus
   pour pouvoir sortir une image GIF
  
*******************************************************************************/

/*

  Copyright 1994 E. I. du Pont de Nemours & Company

  Permission to use, copy, modify, distribute, and sell this software and
  its documentation for any purpose is hereby granted without fee,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of E. I. du Pont de Nemours
  & Company not be used in advertising or publicity pertaining to
  distribution of the software without specific, written prior
  permission.  E. I. du Pont de Nemours & Company makes no representations
  about the suitability of this software for any purpose.  It is provided
  "as is" without express or implied warranty.

  E. I. du Pont de Nemours & Company disclaims all warranties with regard
  to this software, including all implied warranties of merchantability
  and fitness, in no event shall E. I. du Pont de Nemours & Company be
  liable for any special, indirect or consequential damages or any
  damages whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action, arising
  out of or in connection with the use or performance of this software.

*******************************************************************************/

static unsigned char *image;

#define _XOPEN_SOURCE_EXTENDED

static Image *img;

static FILE *file;

#define False  0
#define True  1
#define Max(x,y)  (((x) > (y)) ? (x) : (y))
#define Min(x,y)  (((x) < (y)) ? (x) : (y))


#define MaxMapSize  65535
#define MaxRGB  255


#define color_number  number_colors
#define MaxNodes  266817
#define MaxTreeDepth  8  /* Log2(MaxRGB) */
#define NodesInAList  2048

static gpointer waitData;
static voidDataFunc waitFunc;


typedef struct _Node {
  struct _Node *parent, *child[8];
  unsigned char id, level, children, mid_red, mid_green, mid_blue;
  unsigned long number_colors, number_unique, 
           total_red, total_green, total_blue;
} Node;

typedef struct _Nodes {
  Node nodes[NodesInAList];
  struct _Nodes *next;
} Nodes;

typedef struct _Cube{
  Node *root;
  ColorPacket color, *colormap;
  unsigned int depth;
  unsigned long colors, pruning_threshold, next_pruning_threshold,
         distance, squares[MaxRGB+MaxRGB+1];
  unsigned int shift[MaxTreeDepth+1], nodes, free_nodes, color_number;
  Node *next_node;
  Nodes *node_queue;
} Cube;

static Cube cube;

static unsigned int tree_depth = 8;

static unsigned int Assignment(GString *buffer);
static unsigned int Classification(GString *buffer);
static void ClosestColor(register Node *node);
static void Map(register Node *node);
static unsigned int DitherImage(GString *buffer);
static unsigned int InitializeCube(unsigned int number_pixels, GString *buffer);
static Node *InitializeNode(
  unsigned int id,unsigned int level,Node *parent,
  unsigned int mid_red,unsigned int mid_green,unsigned int mid_blue);
static void PruneChild(register Node *node);
static void PruneLevel(register Node *node);
static void Reduce(register Node *node);
static void Reduction(unsigned int number_colors);
  
static unsigned int LZWEncodeImage(unsigned int data_size);
static void LSBFirstWriteShort(unsigned int value);

int writeViewInGifFormat(FileFormat *format, GString *buffer,
			 char* filename, int width, int height,
			 VisuData *dataObj,
			 voidDataFunc functionWait, gpointer data);


/******************************************************************************/
/******************************************************************************/

void setImage(Image *data)
{
  img = data;
}

/******************************************************************************/

unsigned int Assignment(GString *buffer) {


  img->colormap=(ColorPacket *)
    malloc((unsigned int) cube.colors*sizeof(ColorPacket));
  if (img->colormap == (ColorPacket *) NULL) {
    g_string_append(buffer, _("Unable to quantize image\n"
			      "Memory allocation failed for img->colormap\n"));
      return 1;
  }
  cube.colormap=img->colormap;
  cube.colors=0;
  Map(cube.root);
  img->colors=(unsigned int) cube.colors;
  if (DitherImage(buffer)) return 1;
  return 0;
}

/******************************************************************************/

unsigned int Classification(GString *buffer) {

  register unsigned int i;
  register Node *node;
  register  ColorPacket *p;
  register unsigned int bisect, id, level;

  p=img->pixels;
  for (i=0; i < img->packets; i++) {
    if (cube.nodes > MaxNodes) {
        /* Prune one level if the color tree is too large. */
        PruneLevel(cube.root);
        cube.depth--;
    }
    /* Start at the root and descend the color cube tree. */
    node=cube.root;
    for (level=1; level <= cube.depth; level++) {
      id=(p->red > node->mid_red ? 1 : 0) |
         (p->green > node->mid_green ? 1 : 0) << 1 |
         (p->blue > node->mid_blue ? 1 : 0) << 2;
      if (node->child[id] == (Node *) NULL) {
          /* Set colors of new node to contain pixel. */
          node->children|=1 << id;
          bisect=(unsigned int) (1 << (MaxTreeDepth-level)) >> 1;
          node->child[id]=InitializeNode(id,level,node,
            node->mid_red+(id & 1 ? bisect : -bisect),
            node->mid_green+(id & 2 ? bisect : -bisect),
            node->mid_blue+(id & 4 ? bisect : -bisect));
          if (node->child[id] == (Node *) NULL) {
	    g_string_append_printf(buffer, _("Unable to quantize image\n"
					     "Memory allocation failed"
					     " for node->child[%d]\n"),
                             id);
              return 1;
          }
          if (level == cube.depth) cube.colors++;
      }
      /*
        Record the number of colors represented by this node.  
        Shift by level in the color description tree.
      */
      node=node->child[id];
      node->number_colors+=1 << cube.shift[level];
    }
    /*
      Increment unique color count and sum RGB values for this leaf for later
      derivation of the mean cube color.
    */
    node->number_unique+=1;
    node->total_red+=p->red;
    node->total_green+=p->green;
    node->total_blue+=p->blue;
    p++;
  }
  return 0;
}
 
/******************************************************************************/

void ClosestColor(register Node *node) {

  register unsigned int id;

  /* Traverse any children. */
  if (node->children != 0)
    for (id=0; id < 8; id++)
      if (node->children & (1 << id))
        ClosestColor(node->child[id]);
  if (node->number_unique != 0) {
      register ColorPacket *color;
      register unsigned int blue_distance, green_distance, red_distance;
      register unsigned long distance;

      /* Determine if this color is "closest". */
      color=cube.colormap+node->color_number;
      red_distance=(int) color->red-(int) cube.color.red+MaxRGB;
      green_distance=(int) color->green-(int) cube.color.green+MaxRGB;
      blue_distance=(int) color->blue-(int) cube.color.blue+MaxRGB;
      distance=cube.squares[red_distance]+
               cube.squares[green_distance]+
               cube.squares[blue_distance];
      if (distance < cube.distance) {
          cube.distance=distance;
          cube.color_number=(unsigned short) node->color_number;
      }
  }
}
 
/******************************************************************************/

void Map(register Node *node) {
  register unsigned int id;

  /* Traverse any children*/
  if (node->children != 0)
    for (id=0; id < 8; id++)
      if (node->children & (1 << id))
        Map(node->child[id]);
  if (node->number_unique > 0) {
      /* Map entry is defined by the mean color in this cube. */
      cube.colormap[cube.colors].red=(unsigned char)
        ((node->total_red+(node->number_unique >> 1))/node->number_unique);
      cube.colormap[cube.colors].green=(unsigned char)
        ((node->total_green+(node->number_unique >> 1))/node->number_unique);
      cube.colormap[cube.colors].blue=(unsigned char)
        ((node->total_blue+(node->number_unique >> 1))/node->number_unique);
      node->color_number=cube.colors++;
  }
}
 
/******************************************************************************/

unsigned int DitherImage(GString *buffer) {

#define MaxError  16

  typedef struct {
    int red, green, blue;
  } ErrorPacket;
  ErrorPacket *error;
  int *cache;
  register int blue_error, green_error, red_error, step;
  register Node *node;
  register ColorPacket *q;
  register ErrorPacket *cs, *ns;
  register unsigned char *range_limit;
  register unsigned int id;
  unsigned char blue, green, *range_table, red;
  unsigned int i, x, y;
  unsigned short index;

  
  cache=(int *) malloc((1 << 18)*sizeof(int));
  error=(ErrorPacket *) malloc(((img->columns+2) << 1)*sizeof(ErrorPacket));
  range_table=(unsigned char *) malloc(3*(MaxRGB+1)*sizeof(unsigned char));
  if ((cache == (int *) NULL) || (error == (ErrorPacket *) NULL) ||
      (range_table == (unsigned char *) NULL)) {
    g_string_append(buffer, _("Unable to dither image\n"
			      "Memory allocation failed in DitherImage\n"));
      return 1;
  }
  
  for (i=0; i < (1 << 18); i++) cache[i]=(-1);
  for (i=0; i < ((img->columns+2) << 1); i++) {
    error[i].red=0;
    error[i].green=0;
    error[i].blue=0;
  }
  for (i=0; i <= MaxRGB; i++) {
    range_table[i]=0;
    range_table[i+(MaxRGB+1)]=(unsigned char) i;
    range_table[i+(MaxRGB+1)*2]=MaxRGB;
  }
  range_limit=range_table+(MaxRGB+1);
  
  for (y=0; y < img->rows; y++) {
    q=img->pixels+img->columns*y;
    cs=error+1;
    ns=error+(img->columns+2)+1;
    step=1;
    if (y & 0x01) {
        /* Distribute error right-to-left for odd scanlines. */
        q+=(img->columns-1);
        cs=error+(img->columns+2)+(img->columns-1)+1;
        ns=error+(img->columns-1)+1;
        step=(-1);
    }
    for (x=0; x < img->columns; x++) {
      red_error=(cs->red+8)/16;
      if (red_error > MaxError) red_error=MaxError;
      else if (red_error < -MaxError) red_error=(-MaxError);
      green_error=(cs->green+8)/16;
      if (green_error > MaxError) green_error=MaxError;
      else if (green_error < -MaxError) green_error=(-MaxError);
      blue_error=(cs->blue+8)/16;
      if (blue_error > MaxError) blue_error=MaxError;
      else if (blue_error < -MaxError) blue_error=(-MaxError);
      red=range_limit[q->red+red_error];
      green=range_limit[q->green+green_error];
      blue=range_limit[q->blue+blue_error];
      i=(red >> 2) << 12 | (green >> 2) << 6 | blue >> 2;
      if (cache[i] < 0) {
          /* Identify the deepest node containing the pixel's color. */
          node=cube.root;
          for ( ; ; ) {
            id=(red > node->mid_red ? 1 : 0) |
               (green > node->mid_green ? 1 : 0) << 1 |
               (blue > node->mid_blue ? 1 : 0) << 2;
            if ((node->children & (1 << id)) == 0) break;
            node=node->child[id];
          }
          /* Find closest color among siblings and their children. */
          cube.color.red=red;
          cube.color.green=green;
          cube.color.blue=blue;
          cube.distance=(unsigned long) (~0);
          ClosestColor(node->parent);
          cache[i]=cube.color_number;
      }
      index=(unsigned short) cache[i];
      red_error=(int) red-(int) cube.colormap[index].red;
      green_error=(int) green-(int) cube.colormap[index].green;
      blue_error=(int) blue-(int) cube.colormap[index].blue;
      q->index=index;
      q+=step;
      /* Propagate the error in these proportions:
                Q     7/16
          3/16  5/16  1/16
      */
      cs->red=0;
      cs->green=0;
      cs->blue=0;
      cs+=step;
      cs->red+=7*red_error;
      cs->green+=7*green_error;
      cs->blue+=7*blue_error;
      ns-=step;
      ns->red+=3*red_error;
      ns->green+=3*green_error;
      ns->blue+=3*blue_error;
      ns+=step;
      ns->red+=5*red_error;
      ns->green+=5*green_error;
      ns->blue+=5*blue_error;
      ns+=step;
      ns->red+=red_error;
      ns->green+=green_error;
      ns->blue+=blue_error;
    }
  }
  
  (void)free(range_table);
  (void)free(error);
  (void)free(cache);
  return 0;
}

/******************************************************************************/

unsigned int InitializeCube(unsigned int number_pixels, GString *buffer) {
  
  char c;
  register int i;
  unsigned int bits, level, max_shift;

  cube.node_queue=(Nodes *) NULL;
  cube.nodes=0;
  cube.free_nodes=0;
  cube.depth=Min(tree_depth,8);
  /* Initialize the shift values. */
  c=1;
  for (bits=0; c != (char) 0; bits++) c<<=1;
  for (max_shift=sizeof(unsigned int)*bits; number_pixels != 0; max_shift--)
       number_pixels>>=1;
  for (level=0; level <= cube.depth; level++) {
    cube.shift[level]=max_shift;
    if (max_shift != 0) max_shift--;
  }
  /* Initialize root node. */
  cube.root=InitializeNode(0,0,(Node *) NULL,
     (MaxRGB+1) >> 1,(MaxRGB+1) >> 1, (MaxRGB+1) >> 1);
  if (cube.root == (Node *) NULL) {
    g_string_append(buffer, _("Unable to quantize image\n"
			      "Memory allocation failed for cube.root\n"));
      return 1;
  }
  cube.root->parent=cube.root;
  cube.root->number_colors=(unsigned long) (~0);
  cube.colors=0;
  /* Initialize the square values. */
  for (i=(-MaxRGB); i <= MaxRGB; i++)
    cube.squares[i+MaxRGB]=i*i;
    
  return 0;
}

/******************************************************************************/

Node *InitializeNode(
  unsigned int id,unsigned int level,Node *parent,
  unsigned int mid_red,unsigned int mid_green,unsigned int mid_blue) {
  
  register int i;

  register Node *node;

  if (cube.free_nodes == 0) {
      register Nodes *nodes;

      /* Allocate a new nodes of nodes. */
      nodes=(Nodes *) malloc(sizeof(Nodes));
      if (nodes == (Nodes *) NULL)
        return((Node *) NULL);
      nodes->next=cube.node_queue;
      cube.node_queue=nodes;
      cube.next_node=nodes->nodes;
      cube.free_nodes=NodesInAList;
  }
  cube.nodes++;
  cube.free_nodes--;
  node=cube.next_node++;
  node->parent=parent;
  for (i=0; i < 8; i++)
    node->child[i]=(Node *) NULL;
  node->id=id;
  node->level=level;
  node->children=0;
  node->mid_red=mid_red;
  node->mid_green=mid_green;
  node->mid_blue=mid_blue;
  node->number_colors=0;
  node->number_unique=0;
  node->total_red=0;
  node->total_green=0;
  node->total_blue=0;
  return(node);
}

/******************************************************************************/

void PruneChild(register Node *node) {
  register Node *parent;

  /* Merge color statistics into parent. */
  parent=node->parent;
  parent->children&=~(1 << node->id);
  parent->number_unique+=node->number_unique;
  parent->total_red+=node->total_red;
  parent->total_green+=node->total_green;
  parent->total_blue+=node->total_blue;
  cube.nodes--;
}

/******************************************************************************/

void PruneLevel(register Node *node) {
  register int id;

  /* Traverse any children. */
  if (node->children != 0)
    for (id=0; id < 8; id++)
      if (node->children & (1 << id))
        PruneLevel(node->child[id]);
  if (node->level == cube.depth)
    PruneChild(node);
}

/******************************************************************************/

void Reduce(register Node *node) {
  register unsigned int id;

  /* Traverse any children. */
  if (node->children != 0)
    for (id=0; id < 8; id++)
      if (node->children & (1 << id))
        Reduce(node->child[id]);
        
  /* Node is a colormap entry if it has unique colors. */
  if (node->number_unique > 0) cube.colors++;
  
  /* Find minimum pruning threshold. */
  if (node->number_colors < cube.next_pruning_threshold)
    cube.next_pruning_threshold=node->number_colors;
  if (node->number_colors <= cube.pruning_threshold)
    PruneChild(node); /* Node has a sub-threshold color count */
}

/******************************************************************************/

void Reduction(unsigned int number_colors)
{
  float i;

  i = 0.;
  cube.next_pruning_threshold=1;
  while (cube.colors > number_colors) {
    cube.pruning_threshold=cube.next_pruning_threshold;
    cube.next_pruning_threshold=cube.root->number_colors-1;
    cube.colors=0;
    i += 0.025;
    if (waitFunc && (int)(i * 100.) % 100 == 0 && i < 50.)
      waitFunc(waitData);
    Reduce(cube.root);
  }
  while (waitFunc && i<50.)
    {
      i = i + 1.;
      waitFunc(waitData);
    }
}

/******************************************************************************/

unsigned int QuantizeImage(unsigned int number_colors, GString *buffer,
			   voidDataFunc functionWait, gpointer data)
{
  Nodes *nodes;

  waitFunc = functionWait;
  waitData = data;

  /* Reduce the number of colors in the continuous tone image. */
  if (number_colors > MaxMapSize) number_colors=MaxMapSize;
  if(InitializeCube(img->columns*img->rows, buffer)) return 1;
  
  if(Classification(buffer)) return 1;
  Reduction(number_colors);
  if(Assignment(buffer)) return 1;

  do {
    nodes=cube.node_queue->next;
    (void)free(cube.node_queue);
    cube.node_queue=nodes;
  }
  while (cube.node_queue != (Nodes *) NULL);
  
  return 0;
  
}

/******************************************************************************/
/******************************************************************************/

/* Function LZWEncodeImage compresses an image via LZW-coding. */
unsigned int LZWEncodeImage(unsigned int data_size) {
#define MaxCode(number_bits)  ((1 << (number_bits))-1)
#define MaxHashTable  5003
#define MaxLZWBits  12
#define MaxLZWTable  (1 << MaxLZWBits)
#define LZWOutputCode(code) \
{ \
  if (bits > 0) datum|=((long) code << bits); \
  else datum=(long) code; \
  bits+=number_bits; \
  while (bits >= 8) { \
    packet[byte_count++]=(unsigned char) (datum & 0xff); \
    if (byte_count >= 254) { \
        (void) fputc(byte_count,file); \
        (void) fwrite((char *) packet,1,byte_count,file); \
        byte_count=0; \
    } \
    datum>>=8; \
    bits-=8; \
  } \
  if (free_code > max_code) { \
      number_bits++; \
      if (number_bits == MaxLZWBits)  max_code=MaxLZWTable; \
      else max_code=MaxCode(number_bits); \
  } \
}

  int bits, byte_count, next_pixel, number_bits;
  long datum;
  register unsigned i;
  register int displacement, j;
  register ColorPacket *p;
  short clear_code, end_of_information_code;
  short free_code, *hash_code, *hash_prefix, index, max_code, waiting_code;
  unsigned char *packet, *hash_suffix;

  

  packet=(unsigned char *) malloc(256*sizeof(unsigned char));
  hash_code=(short *) malloc(MaxHashTable*sizeof(short));
  hash_prefix=(short *) malloc(MaxHashTable*sizeof(short));
  hash_suffix=(unsigned char *) malloc(MaxHashTable*sizeof(unsigned char));
  if ((!packet) || (!hash_code) || (!hash_prefix) || (!hash_suffix))
     return(False);
  
  number_bits=data_size;
  max_code=MaxCode(number_bits);
  clear_code=((short) 1 << (data_size-1));
  end_of_information_code=clear_code+1;
  free_code=clear_code+2;
  byte_count=0;
  datum=0;
  bits=0;
  for (i=0; i < MaxHashTable; i++) hash_code[i]=0;
  LZWOutputCode(clear_code);
  
  p=img->pixels;
  waiting_code=p->index;
  for (i=1; i < (img->columns*img->rows); i++) {
    
    if (waitFunc && i % (img->columns*img->rows / 50) == 0)
      waitFunc(waitData);

    p++;
    index=p->index & 0xff;
    j=(int) ((int) index << (MaxLZWBits-8))+waiting_code;
    if (j >= MaxHashTable) j-=MaxHashTable;
    if (hash_code[j] > 0) {
        if ((hash_prefix[j] == waiting_code) && (hash_suffix[j] == index))  {
            waiting_code=hash_code[j];
            continue;
        }
        if (j == 0) displacement=1;
        else displacement=MaxHashTable-j;
        next_pixel=False;
        for ( ; ; ) {
          j-=displacement;
          if (j < 0) j+=MaxHashTable;
          if (hash_code[j] == 0)  break;
          if ((hash_prefix[j] == waiting_code) && (hash_suffix[j] == index)) {
              waiting_code=hash_code[j];
              next_pixel=True;
              break;
          }
        }
        if (next_pixel == True) continue;
    }
    LZWOutputCode(waiting_code);
    if (free_code < MaxLZWTable) {
        hash_code[j]=free_code++;
        hash_prefix[j]=waiting_code;
        hash_suffix[j]=(unsigned char)index;
    }
    else {
        for (j=0; j < MaxHashTable; j++)  hash_code[j]=0;
        free_code=clear_code+2;
        LZWOutputCode(clear_code);
        number_bits=data_size;
        max_code=MaxCode(number_bits);
    }
    waiting_code=index;
  }
  
  
  LZWOutputCode(waiting_code);
  LZWOutputCode(end_of_information_code);
  if (bits > 0) {
      /* Add a character to current packet. */
      packet[byte_count++]=(unsigned char) (datum & 0xff);
      if (byte_count >= 254)  {
          (void) fputc(byte_count,file);
          (void) fwrite((char *) packet,1,byte_count,file);
          byte_count=0;
      }
  }
  
  if (byte_count > 0) {
      (void) fputc(byte_count,file);
      (void) fwrite((char *) packet,1,byte_count,file);
  }
  
  (void)free(hash_suffix);
  (void)free(hash_prefix);
  (void)free(hash_code);
  (void)free(packet);
  
  if (i < img->packets) return(False);
  return(True);
  
}


/******************************************************************************/

/* LSBFirstWriteShort writes a long value as a 16 bit quantity in
   least-significant byte first order.
*/
void LSBFirstWriteShort(unsigned int value) {

  unsigned char buffer[2];

  buffer[0]=(unsigned char) (value);
  buffer[1]=(unsigned char) ((value) >> 8);
  (void) fwrite((char *) buffer,1,2,file);
}


/******************************************************************************/

void SyncImage(void) {

  register unsigned int i;
  register ColorPacket *p;
  register unsigned short index;

  p=img->pixels;
  for (i=0; i < img->packets; i++) {
    index=p->index;
    p->red=img->colormap[index].red;
    p->green=img->colormap[index].green;
    p->blue=img->colormap[index].blue;
    p++;
  }
}

/******************************************************************************/

static void write_comment_comm(unsigned char comm[]) {

   size_t size;   
   unsigned char c[256];
   
   /* Extension Introducer */
   c[0] = 0x21;
   (void)fwrite(c, sizeof(unsigned char), 1, file);
   
   /* Comment Label */
   c[0] = 0xFE;
   (void)fwrite(c, sizeof(unsigned char), 1, file);
   
   /* Data Size ( entre 1 et 255 ) */
   size = strlen((char *)comm);
   c[0] = (unsigned char)size;
   (void)fwrite(c, sizeof(unsigned char), 1, file);
   
   /* Data */
   (void)fwrite(comm, sizeof(unsigned char), size, file);
   
   /* Block Terminator */
   c[0] = '\0';
   (void)fwrite(c, sizeof(unsigned char), 1, file);
   
}

/******************************************************************************/

DumpType* initDumpToGif()
{
  DumpType *gif;
  char *typeGIF[] = {"*.gif", (char*)0};
  #define descrGIF _("Gif (256 colors) file")
  FileFormat* fmt;

  gif = malloc(sizeof(DumpType));
  if (!gif)
    {
      allocationProblems();
      exit(1);
    }
  fmt = fileFormatNew(descrGIF, typeGIF);
  if (!fmt)
    g_error("Can't initialize the GIF dump module, aborting.\n");

  gif->fileType = fmt;
  gif->writeFunc = writeViewInGifFormat;

  waitData = (gpointer)0;
  waitFunc = (voidDataFunc)0;

  return gif;
}


int writeViewInGifFormat(FileFormat *format, GString *buffer,
			 char* filename, int width, int height,
			 VisuData *dataObj,
			 voidDataFunc functionWait, gpointer data)
{
  register unsigned int i;
  register ColorPacket *q;
  register unsigned char *p;
  unsigned char bits_per_pixel, c;
  unsigned int status;

  image = dumpViewToImage(dataObj, buffer, &width, &height);
  if (!image)
    return 1;

  DBG_fprintf(stderr, "Dump Gif : begin export in %dx%d...\n", width, height);

  file = fopen(filename, "wb");
  if(!file)
    {
      g_string_append(buffer, _("Cannot open file (to write in)\n"));
      free(image);
      return 1;
    }

  waitData = data;
  waitFunc = functionWait;

  img=g_malloc(sizeof(Image));

  img->columns = width;
  img->rows = height;
  img->packets=img->columns*img->rows;
  img->pixels=(ColorPacket *)
    malloc((unsigned int) img->packets*sizeof(ColorPacket));
  if (!img->pixels)
    {
      g_string_append(buffer, _("Memory allocation error for img->packets\n"));
      return 1;
    }
  q=img->pixels;
  p=image;
  for (i=0; i < img->packets; i++) {
    q->red=(*p++);
    q->green=(*p++);
    q->blue=(*p++);
    q->index=0;
    q++;
  }
  
  if(QuantizeImage(256, buffer, waitFunc, data))
    return 1;
  SyncImage();
   
  for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
    if ((1 << bits_per_pixel) >= (int)img->colors) break;
  
  
  (void) fwrite("GIF89a",1,6,file);
  LSBFirstWriteShort(img->columns);
  LSBFirstWriteShort(img->rows);
  c=0x80;  /* global colormap */
  c|=(8-1) << 4;  /* color resolution */
  c|=(bits_per_pixel-1);   /* size of global colormap */
  (void) fputc((int)(char) c,file);
  (void) fputc(0x0,file);  /* background color */
  (void) fputc(0x0,file);  /* reserved */
  
  
  for (i=0; i < img->colors; i++) {
    (void) fputc((int)(char) img->colormap[i].red,file);
    (void) fputc((int)(char) img->colormap[i].green,file);
    (void) fputc((int)(char) img->colormap[i].blue,file);
  }
  for ( ; i < (unsigned int)(1 << bits_per_pixel) ; i++) {
    (void) fputc(0x0,file);
    (void) fputc(0x0,file);
    (void) fputc(0x0,file);
  }
  /* écriture du commentaire */
  write_comment_comm( 
		     (unsigned char *)"Image créée par le programme V_Sim\n"
		     "Auteur : L. BILLARD");
      
  (void) fputc(',',file);  /* image separator */
  
  
  LSBFirstWriteShort(0);
  LSBFirstWriteShort(0);
  LSBFirstWriteShort(img->columns);
  LSBFirstWriteShort(img->rows);
  (void) fputc(0x0,file);
  c=Max(bits_per_pixel,2);
  (void) fputc((int)(char) c,file);
  status=LZWEncodeImage(Max(bits_per_pixel,2)+1);
  if (status == False) {
    g_string_append(buffer, _("Unable to write image\n"
			      "Memory allocation failed for LZWEncodeImage\n"));
    return 1;
  }
  (void) fputc(0x0,file);
  (void) fputc(';',file); /* terminator */
   
  (void) fclose(file);
   
  (void)free(img->pixels);
  (void)free(img->colormap);
  (void)free(img);
  
  free(image);
  return 0;
}
