1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Luc BILLARD et Damien
3 	CALISTE, laboratoire L_Sim, (2001-2005)
4 
5 	Adresse mèl :
6 	BILLARD, non joignable par mèl ;
7 	CALISTE, damien P caliste AT cea P fr.
8 
9 	Ce logiciel est un programme informatique servant à visualiser des
10 	structures atomiques dans un rendu pseudo-3D.
11 
12 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
13 	respectant les principes de diffusion des logiciels libres. Vous pouvez
14 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
15 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
16 	sur le site "http://www.cecill.info".
17 
18 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
19 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
20 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
21 */
22 
23 /*   LICENCE SUM UP
24 	Copyright CEA, contributors : Luc BILLARD et Damien
25 	CALISTE, laboratoire L_Sim, (2001-2005)
26 
27 	E-mail address:
28 	BILLARD, not reachable any more ;
29 	CALISTE, damien P caliste AT cea P fr.
30 
31 	This software is a computer program whose purpose is to visualize atomic
32 	configurations in 3D.
33 
34 	This software is governed by the CeCILL  license under French law and
35 	abiding by the rules of distribution of free software.  You can  use,
36 	modify and/ or redistribute the software under the terms of the CeCILL
37 	license as circulated by CEA, CNRS and INRIA at the following URL
38 	"http://www.cecill.info".
39 
40 	The fact that you are presently reading this means that you have had
41 	knowledge of the CeCILL license and that you accept its terms. You can
42 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
43 */
44 #include "dumpToGif.h"
45 
46 #include "stdlib.h"
47 #include "stdio.h"
48 #include "string.h"
49 #include <visu_tools.h>
50 
51 #include <glib.h>
52 /* #include <unistd.h> */
53 #include <time.h>
54 
55 /**
56  * SECTION:dumpToGif
57  * @short_description: add an export capability into GIF files.
58  *
59  * <para>This provides a write routine to export V_Sim views into GIF
60  * files.</para>
61  *
62  * <para>Most of the routines used there have been modified by
63  * L. Billard (1997 - 2001) from the original ones taken from the
64  * ImageMagick package of cristy@dupont.com.</para>
65  *
66  * <para>The goal of this is to reduce the colour span into 256 to be
67  * able to output as GIF file.</para>
68  *
69  * <note>
70  * <para>Copyright 1994 E. I. du Pont de Nemours & Company</para>
71  * <para>Permission to use, copy, modify, distribute, and sell this
72  * software and its documentation for any purpose is hereby granted
73  * without fee, provided that the above copyright notice appear in all
74  * copies and that both that copyright notice and this permission
75  * notice appear in supporting documentation, and that the name of
76  * E. I. du Pont de Nemours & Company not be used in advertising or
77  * publicity pertaining to distribution of the software without
78  * specific, written prior permission.  E. I. du Pont de Nemours &
79  * Company makes no representations about the suitability of this
80  * software for any purpose.  It is provided "as is" without express
81  * or implied warranty.</para>
82  * <para>E. I. du Pont de Nemours & Company disclaims all warranties
83  * with regard to this software, including all implied warranties of
84  * merchantability and fitness, in no event shall E. I. du Pont de
85  * Nemours & Company be liable for any special, indirect or
86  * consequential damages or any damages whatsoever resulting from loss
87  * of use, data or profits, whether in an action of contract,
88  * negligence or other tortious action, arising out of or in
89  * connection with the use or performance of this software.</para>
90  * </note>
91  */
92 
93 static unsigned char *image;
94 
95 #define _XOPEN_SOURCE_EXTENDED
96 
97 static Image *img;
98 
99 static FILE *file;
100 
101 #define False  0
102 #define True  1
103 #define Max(x,y)  (((x) > (y)) ? (x) : (y))
104 #define Min(x,y)  (((x) < (y)) ? (x) : (y))
105 
106 
107 #define MaxMapSize  65535
108 #define MaxRGB  255
109 
110 
111 #define color_number  number_colors
112 #define MaxNodes  266817
113 #define MaxTreeDepth  8  /* Log2(MaxRGB) */
114 #define NodesInAList  2048
115 
116 static gpointer waitData;
117 static ToolVoidDataFunc waitFunc;
118 
119 
120 typedef struct _Node {
121   struct _Node *parent, *child[8];
122   unsigned char id, level, children, mid_red, mid_green, mid_blue;
123   unsigned long number_colors, number_unique,
124            total_red, total_green, total_blue;
125 } Node;
126 
127 typedef struct _Nodes {
128   Node nodes[NodesInAList];
129   struct _Nodes *next;
130 } Nodes;
131 
132 typedef struct _Cube{
133   Node *root;
134   ColorPacket color, *colormap;
135   guint depth;
136   unsigned long colors, pruning_threshold, next_pruning_threshold,
137          distance, squares[MaxRGB+MaxRGB+1];
138   guint shift[MaxTreeDepth+1], nodes, free_nodes, color_number;
139   Node *next_node;
140   Nodes *node_queue;
141 } Cube;
142 
143 static Cube cube;
144 
145 static guint tree_depth = 8;
146 
147 static guint Assignment();
148 static guint Classification(GError **error);
149 static void ClosestColor(register Node *node);
150 static void Map(register Node *node);
151 static guint DitherImage();
152 static guint InitializeCube(guint number_pixels, GError **error);
153 static Node *InitializeNode(
154   guint id,guint level,Node *parent,
155   guint mid_red,guint mid_green,guint mid_blue);
156 static void PruneChild(register Node *node);
157 static void PruneLevel(register Node *node);
158 static void Reduce(register Node *node);
159 static void Reduction(guint number_colors);
160 
161 static guint LZWEncodeImage(guint data_size);
162 static void LSBFirstWriteShort(guint value);
163 
164 static gboolean writeViewInGifFormat(ToolFileFormat *format, const char* filename,
165 				      VisuGlNodeScene *scene, guint width, guint height,
166 				      GError **error, ToolVoidDataFunc functionWait,
167                                       gpointer data);
168 
169 
170 /******************************************************************************/
171 /******************************************************************************/
172 
dumpToGif_setImage(Image * data)173 void dumpToGif_setImage(Image *data)
174 {
175   img = data;
176 }
177 
178 /******************************************************************************/
179 
Assignment()180 guint Assignment() {
181 
182 
183   img->colormap=g_malloc(cube.colors*sizeof(ColorPacket));
184   cube.colormap=img->colormap;
185   cube.colors=0;
186   Map(cube.root);
187   img->colors=(guint) cube.colors;
188   if (DitherImage()) return 1;
189   return 0;
190 }
191 
192 /******************************************************************************/
193 
Classification(GError ** error)194 guint Classification(GError **error) {
195 
196   register guint i;
197   register Node *node;
198   register  ColorPacket *p;
199   register guint bisect, id, level;
200 
201   p=img->pixels;
202   for (i=0; i < img->packets; i++) {
203     if (cube.nodes > MaxNodes) {
204         /* Prune one level if the color tree is too large. */
205         PruneLevel(cube.root);
206         cube.depth--;
207     }
208     /* Start at the root and descend the color cube tree. */
209     node=cube.root;
210     for (level=1; level <= cube.depth; level++) {
211       id=(p->red > node->mid_red ? 1 : 0) |
212          (p->green > node->mid_green ? 1 : 0) << 1 |
213          (p->blue > node->mid_blue ? 1 : 0) << 2;
214       if (node->child[id] == (Node *) NULL) {
215           /* Set colors of new node to contain pixel. */
216           node->children|=1 << id;
217           bisect=(guint) (1 << (MaxTreeDepth-level)) >> 1;
218           node->child[id]=InitializeNode(id,level,node,
219             node->mid_red+(id & 1 ? bisect : -bisect),
220             node->mid_green+(id & 2 ? bisect : -bisect),
221             node->mid_blue+(id & 4 ? bisect : -bisect));
222           if (node->child[id] == (Node *) NULL)
223 	    {
224 	      *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_ENCODE,
225 				   _("Unable to quantize image, "
226 				     "initialisation failed for node child %d."), id);
227               return 1;
228 	    }
229           if (level == cube.depth) cube.colors++;
230       }
231       /*
232         Record the number of colors represented by this node.
233         Shift by level in the color description tree.
234       */
235       node=node->child[id];
236       node->number_colors+=1 << cube.shift[level];
237     }
238     /*
239       Increment unique color count and sum RGB values for this leaf for later
240       derivation of the mean cube color.
241     */
242     node->number_unique+=1;
243     node->total_red+=p->red;
244     node->total_green+=p->green;
245     node->total_blue+=p->blue;
246     p++;
247   }
248   return 0;
249 }
250 
251 /******************************************************************************/
252 
ClosestColor(register Node * node)253 void ClosestColor(register Node *node) {
254 
255   register guint id;
256 
257   /* Traverse any children. */
258   if (node->children != 0)
259     for (id=0; id < 8; id++)
260       if (node->children & (1 << id))
261         ClosestColor(node->child[id]);
262   if (node->number_unique != 0) {
263       register ColorPacket *color;
264       register guint blue_distance, green_distance, red_distance;
265       register unsigned long distance;
266 
267       /* Determine if this color is "closest". */
268       color=cube.colormap+node->color_number;
269       red_distance=(int) color->red-(int) cube.color.red+MaxRGB;
270       green_distance=(int) color->green-(int) cube.color.green+MaxRGB;
271       blue_distance=(int) color->blue-(int) cube.color.blue+MaxRGB;
272       distance=cube.squares[red_distance]+
273                cube.squares[green_distance]+
274                cube.squares[blue_distance];
275       if (distance < cube.distance) {
276           cube.distance=distance;
277           cube.color_number=(unsigned short) node->color_number;
278       }
279   }
280 }
281 
282 /******************************************************************************/
283 
Map(register Node * node)284 void Map(register Node *node) {
285   register guint id;
286 
287   /* Traverse any children*/
288   if (node->children != 0)
289     for (id=0; id < 8; id++)
290       if (node->children & (1 << id))
291         Map(node->child[id]);
292   if (node->number_unique > 0) {
293       /* Map entry is defined by the mean color in this cube. */
294       cube.colormap[cube.colors].red=(unsigned char)
295         ((node->total_red+(node->number_unique >> 1))/node->number_unique);
296       cube.colormap[cube.colors].green=(unsigned char)
297         ((node->total_green+(node->number_unique >> 1))/node->number_unique);
298       cube.colormap[cube.colors].blue=(unsigned char)
299         ((node->total_blue+(node->number_unique >> 1))/node->number_unique);
300       node->color_number=cube.colors++;
301   }
302 }
303 
304 /******************************************************************************/
305 
DitherImage()306 guint DitherImage() {
307 
308 #define MaxError  16
309 
310   typedef struct {
311     int red, green, blue;
312   } ErrorPacket;
313   ErrorPacket *error;
314   int *cache;
315   register int blue_error, green_error, red_error, step;
316   register Node *node;
317   register ColorPacket *q;
318   register ErrorPacket *cs, *ns;
319   register unsigned char *range_limit;
320   register guint id;
321   unsigned char blue, green, *range_table, red;
322   guint i, x, y;
323   unsigned short index;
324 
325 
326   cache=g_malloc((1 << 18)*sizeof(int));
327   error=g_malloc(((img->columns+2) << 1)*sizeof(ErrorPacket));
328   range_table=g_malloc(3*(MaxRGB+1)*sizeof(unsigned char));
329 
330   for (i=0; i < (1 << 18); i++) cache[i]=(-1);
331   for (i=0; i < ((img->columns+2) << 1); i++) {
332     error[i].red=0;
333     error[i].green=0;
334     error[i].blue=0;
335   }
336   for (i=0; i <= MaxRGB; i++) {
337     range_table[i]=0;
338     range_table[i+(MaxRGB+1)]=(unsigned char) i;
339     range_table[i+(MaxRGB+1)*2]=MaxRGB;
340   }
341   range_limit=range_table+(MaxRGB+1);
342 
343   for (y=0; y < img->rows; y++) {
344     q=img->pixels+img->columns*y;
345     cs=error+1;
346     ns=error+(img->columns+2)+1;
347     step=1;
348     if (y & 0x01) {
349         /* Distribute error right-to-left for odd scanlines. */
350         q+=(img->columns-1);
351         cs=error+(img->columns+2)+(img->columns-1)+1;
352         ns=error+(img->columns-1)+1;
353         step=(-1);
354     }
355     for (x=0; x < img->columns; x++) {
356       red_error=(cs->red+8)/16;
357       if (red_error > MaxError) red_error=MaxError;
358       else if (red_error < -MaxError) red_error=(-MaxError);
359       green_error=(cs->green+8)/16;
360       if (green_error > MaxError) green_error=MaxError;
361       else if (green_error < -MaxError) green_error=(-MaxError);
362       blue_error=(cs->blue+8)/16;
363       if (blue_error > MaxError) blue_error=MaxError;
364       else if (blue_error < -MaxError) blue_error=(-MaxError);
365       red=range_limit[q->red+red_error];
366       green=range_limit[q->green+green_error];
367       blue=range_limit[q->blue+blue_error];
368       i=(red >> 2) << 12 | (green >> 2) << 6 | blue >> 2;
369       if (cache[i] < 0) {
370           /* Identify the deepest node containing the pixel's color. */
371           node=cube.root;
372           for ( ; ; ) {
373             id=(red > node->mid_red ? 1 : 0) |
374                (green > node->mid_green ? 1 : 0) << 1 |
375                (blue > node->mid_blue ? 1 : 0) << 2;
376             if ((node->children & (1 << id)) == 0) break;
377             node=node->child[id];
378           }
379           /* Find closest color among siblings and their children. */
380           cube.color.red=red;
381           cube.color.green=green;
382           cube.color.blue=blue;
383           cube.distance=(unsigned long) (~0);
384           ClosestColor(node->parent);
385           cache[i]=cube.color_number;
386       }
387       index=(unsigned short) cache[i];
388       red_error=(int) red-(int) cube.colormap[index].red;
389       green_error=(int) green-(int) cube.colormap[index].green;
390       blue_error=(int) blue-(int) cube.colormap[index].blue;
391       q->index=index;
392       q+=step;
393       /* Propagate the error in these proportions:
394                 Q     7/16
395           3/16  5/16  1/16
396       */
397       cs->red=0;
398       cs->green=0;
399       cs->blue=0;
400       cs+=step;
401       cs->red+=7*red_error;
402       cs->green+=7*green_error;
403       cs->blue+=7*blue_error;
404       ns-=step;
405       ns->red+=3*red_error;
406       ns->green+=3*green_error;
407       ns->blue+=3*blue_error;
408       ns+=step;
409       ns->red+=5*red_error;
410       ns->green+=5*green_error;
411       ns->blue+=5*blue_error;
412       ns+=step;
413       ns->red+=red_error;
414       ns->green+=green_error;
415       ns->blue+=blue_error;
416     }
417   }
418 
419   g_free(range_table);
420   g_free(error);
421   g_free(cache);
422   return 0;
423 }
424 
425 /******************************************************************************/
426 
InitializeCube(guint number_pixels,GError ** error)427 guint InitializeCube(guint number_pixels, GError **error) {
428 
429   char c;
430   register int i;
431   guint bits, level, max_shift;
432 
433   cube.node_queue=(Nodes *) NULL;
434   cube.nodes=0;
435   cube.free_nodes=0;
436   cube.depth=Min(tree_depth,8);
437   /* Initialize the shift values. */
438   c=1;
439   for (bits=0; c != (char) 0; bits++) c<<=1;
440   for (max_shift=sizeof(guint)*bits; number_pixels != 0; max_shift--)
441        number_pixels>>=1;
442   for (level=0; level <= cube.depth; level++) {
443     cube.shift[level]=max_shift;
444     if (max_shift != 0) max_shift--;
445   }
446   /* Initialize root node. */
447   cube.root=InitializeNode(0,0,(Node *) NULL,
448      (MaxRGB+1) >> 1,(MaxRGB+1) >> 1, (MaxRGB+1) >> 1);
449   if (cube.root == (Node *) NULL)
450     {
451       *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_ENCODE,
452 			   _("Unable to quantize image, initialisation failed."));
453       return 1;
454     }
455   cube.root->parent=cube.root;
456   cube.root->number_colors=(unsigned long) (~0);
457   cube.colors=0;
458   /* Initialize the square values. */
459   for (i=(-MaxRGB); i <= MaxRGB; i++)
460     cube.squares[i+MaxRGB]=i*i;
461 
462   return 0;
463 }
464 
465 /******************************************************************************/
466 
InitializeNode(guint id,guint level,Node * parent,guint mid_red,guint mid_green,guint mid_blue)467 Node *InitializeNode(
468   guint id,guint level,Node *parent,
469   guint mid_red,guint mid_green,guint mid_blue) {
470 
471   register int i;
472 
473   register Node *node;
474 
475   if (cube.free_nodes == 0) {
476       register Nodes *nodes;
477 
478       /* Allocate a new nodes of nodes. */
479       nodes=(Nodes *) malloc(sizeof(Nodes));
480       if (nodes == (Nodes *) NULL)
481         return((Node *) NULL);
482       nodes->next=cube.node_queue;
483       cube.node_queue=nodes;
484       cube.next_node=nodes->nodes;
485       cube.free_nodes=NodesInAList;
486   }
487   cube.nodes++;
488   cube.free_nodes--;
489   node=cube.next_node++;
490   node->parent=parent;
491   for (i=0; i < 8; i++)
492     node->child[i]=(Node *) NULL;
493   node->id=id;
494   node->level=level;
495   node->children=0;
496   node->mid_red=mid_red;
497   node->mid_green=mid_green;
498   node->mid_blue=mid_blue;
499   node->number_colors=0;
500   node->number_unique=0;
501   node->total_red=0;
502   node->total_green=0;
503   node->total_blue=0;
504   return(node);
505 }
506 
507 /******************************************************************************/
508 
PruneChild(register Node * node)509 void PruneChild(register Node *node) {
510   register Node *parent;
511 
512   /* Merge color statistics into parent. */
513   parent=node->parent;
514   parent->children&=~(1 << node->id);
515   parent->number_unique+=node->number_unique;
516   parent->total_red+=node->total_red;
517   parent->total_green+=node->total_green;
518   parent->total_blue+=node->total_blue;
519   cube.nodes--;
520 }
521 
522 /******************************************************************************/
523 
PruneLevel(register Node * node)524 void PruneLevel(register Node *node) {
525   register int id;
526 
527   /* Traverse any children. */
528   if (node->children != 0)
529     for (id=0; id < 8; id++)
530       if (node->children & (1 << id))
531         PruneLevel(node->child[id]);
532   if (node->level == cube.depth)
533     PruneChild(node);
534 }
535 
536 /******************************************************************************/
537 
Reduce(register Node * node)538 void Reduce(register Node *node) {
539   register guint id;
540 
541   /* Traverse any children. */
542   if (node->children != 0)
543     for (id=0; id < 8; id++)
544       if (node->children & (1 << id))
545         Reduce(node->child[id]);
546 
547   /* Node is a colormap entry if it has unique colors. */
548   if (node->number_unique > 0) cube.colors++;
549 
550   /* Find minimum pruning threshold. */
551   if (node->number_colors < cube.next_pruning_threshold)
552     cube.next_pruning_threshold=node->number_colors;
553   if (node->number_colors <= cube.pruning_threshold)
554     PruneChild(node); /* Node has a sub-threshold color count */
555 }
556 
557 /******************************************************************************/
558 
Reduction(guint number_colors)559 void Reduction(guint number_colors)
560 {
561   float i;
562 
563   i = 0.;
564   cube.next_pruning_threshold=1;
565   while (cube.colors > number_colors) {
566     cube.pruning_threshold=cube.next_pruning_threshold;
567     cube.next_pruning_threshold=cube.root->number_colors-1;
568     cube.colors=0;
569     i += 0.025;
570     if (waitFunc && (int)(i * 100.) % 100 == 0 && i < 50.)
571       waitFunc(waitData);
572     Reduce(cube.root);
573   }
574   while (waitFunc && i<50.)
575     {
576       i = i + 1.;
577       waitFunc(waitData);
578     }
579 }
580 
581 /******************************************************************************/
582 
dumpToGif_quantizeImage(guint number_colors,GError ** error,ToolVoidDataFunc functionWait,gpointer data)583 guint dumpToGif_quantizeImage(guint number_colors, GError **error,
584 				     ToolVoidDataFunc functionWait, gpointer data)
585 {
586   Nodes *nodes;
587 
588   waitFunc = functionWait;
589   waitData = data;
590 
591   /* Reduce the number of colors in the continuous tone image. */
592   if (number_colors > MaxMapSize) number_colors=MaxMapSize;
593   if(InitializeCube(img->columns*img->rows, error)) return 1;
594 
595   if(Classification(error)) return 1;
596   Reduction(number_colors);
597   if(Assignment(error)) return 1;
598 
599   do {
600     nodes=cube.node_queue->next;
601     (void)free(cube.node_queue);
602     cube.node_queue=nodes;
603   }
604   while (cube.node_queue != (Nodes *) NULL);
605 
606   return 0;
607 
608 }
609 
610 /******************************************************************************/
611 /******************************************************************************/
612 
613 /* Function LZWEncodeImage compresses an image via LZW-coding. */
LZWEncodeImage(guint data_size)614 guint LZWEncodeImage(guint data_size) {
615 #define MaxCode(number_bits)  ((1 << (number_bits))-1)
616 #define MaxHashTable  5003
617 #define MaxLZWBits  12
618 #define MaxLZWTable  (1 << MaxLZWBits)
619 #define LZWOutputCode(code) \
620 { \
621   if (bits > 0) datum|=((long) code << bits); \
622   else datum=(long) code; \
623   bits+=number_bits; \
624   while (bits >= 8) { \
625     packet[byte_count++]=(unsigned char) (datum & 0xff); \
626     if (byte_count >= 254) { \
627         (void) fputc(byte_count,file); \
628         (void) fwrite((char *) packet,1,byte_count,file); \
629         byte_count=0; \
630     } \
631     datum>>=8; \
632     bits-=8; \
633   } \
634   if (free_code > max_code) { \
635       number_bits++; \
636       if (number_bits == MaxLZWBits)  max_code=MaxLZWTable; \
637       else max_code=MaxCode(number_bits); \
638   } \
639 }
640 
641   int bits, byte_count, next_pixel, number_bits;
642   long datum;
643   register unsigned i;
644   register int displacement, j;
645   register ColorPacket *p;
646   short clear_code, end_of_information_code;
647   short free_code, *hash_code, *hash_prefix, index, max_code, waiting_code;
648   unsigned char *packet, *hash_suffix;
649 
650 
651 
652   packet=g_malloc(256*sizeof(unsigned char));
653   hash_code=g_malloc(MaxHashTable*sizeof(short));
654   hash_prefix=g_malloc(MaxHashTable*sizeof(short));
655   hash_suffix=g_malloc(MaxHashTable*sizeof(unsigned char));
656 
657   number_bits=data_size;
658   max_code=MaxCode(number_bits);
659   clear_code=((short) 1 << (data_size-1));
660   end_of_information_code=clear_code+1;
661   free_code=clear_code+2;
662   byte_count=0;
663   datum=0;
664   bits=0;
665   for (i=0; i < MaxHashTable; i++) hash_code[i]=0;
666   LZWOutputCode(clear_code);
667 
668   p=img->pixels;
669   waiting_code=p->index;
670   for (i=1; i < (img->columns*img->rows); i++) {
671 
672     if (waitFunc && i % (img->columns*img->rows / 50) == 0)
673       waitFunc(waitData);
674 
675     p++;
676     index=p->index & 0xff;
677     j=(int) ((int) index << (MaxLZWBits-8))+waiting_code;
678     if (j >= MaxHashTable) j-=MaxHashTable;
679     if (hash_code[j] > 0) {
680         if ((hash_prefix[j] == waiting_code) && (hash_suffix[j] == index))  {
681             waiting_code=hash_code[j];
682             continue;
683         }
684         if (j == 0) displacement=1;
685         else displacement=MaxHashTable-j;
686         next_pixel=False;
687         for ( ; ; ) {
688           j-=displacement;
689           if (j < 0) j+=MaxHashTable;
690           if (hash_code[j] == 0)  break;
691           if ((hash_prefix[j] == waiting_code) && (hash_suffix[j] == index)) {
692               waiting_code=hash_code[j];
693               next_pixel=True;
694               break;
695           }
696         }
697         if (next_pixel == True) continue;
698     }
699     LZWOutputCode(waiting_code);
700     if (free_code < MaxLZWTable) {
701         hash_code[j]=free_code++;
702         hash_prefix[j]=waiting_code;
703         hash_suffix[j]=(unsigned char)index;
704     }
705     else {
706         for (j=0; j < MaxHashTable; j++)  hash_code[j]=0;
707         free_code=clear_code+2;
708         LZWOutputCode(clear_code);
709         number_bits=data_size;
710         max_code=MaxCode(number_bits);
711     }
712     waiting_code=index;
713   }
714 
715 
716   LZWOutputCode(waiting_code);
717   LZWOutputCode(end_of_information_code);
718   if (bits > 0) {
719       /* Add a character to current packet. */
720       packet[byte_count++]=(unsigned char) (datum & 0xff);
721       if (byte_count >= 254)  {
722           (void) fputc(byte_count,file);
723           (void) fwrite((char *) packet,1,byte_count,file);
724           byte_count=0;
725       }
726   }
727 
728   if (byte_count > 0) {
729       (void) fputc(byte_count,file);
730       (void) fwrite((char *) packet,1,byte_count,file);
731   }
732 
733   g_free(hash_suffix);
734   g_free(hash_prefix);
735   g_free(hash_code);
736   g_free(packet);
737 
738   if (i < img->packets) return(False);
739   return(True);
740 
741 }
742 
743 
744 /******************************************************************************/
745 
746 /* LSBFirstWriteShort writes a long value as a 16 bit quantity in
747    least-significant byte first order.
748 */
LSBFirstWriteShort(guint value)749 void LSBFirstWriteShort(guint value) {
750 
751   unsigned char buffer[2];
752 
753   buffer[0]=(unsigned char) (value);
754   buffer[1]=(unsigned char) ((value) >> 8);
755   (void) fwrite((char *) buffer,1,2,file);
756 }
757 
758 
759 /******************************************************************************/
760 
dumpToGif_syncImage(void)761 void dumpToGif_syncImage(void) {
762 
763   register guint i;
764   register ColorPacket *p;
765   register unsigned short index;
766 
767   p=img->pixels;
768   for (i=0; i < img->packets; i++) {
769     index=p->index;
770     p->red=img->colormap[index].red;
771     p->green=img->colormap[index].green;
772     p->blue=img->colormap[index].blue;
773     p++;
774   }
775 }
776 
777 /******************************************************************************/
778 
write_comment_comm(unsigned char comm[])779 static void write_comment_comm(unsigned char comm[]) {
780 
781    size_t size;
782    unsigned char c[256];
783 
784    /* Extension Introducer */
785    c[0] = 0x21;
786    (void)fwrite(c, sizeof(unsigned char), 1, file);
787 
788    /* Comment Label */
789    c[0] = 0xFE;
790    (void)fwrite(c, sizeof(unsigned char), 1, file);
791 
792    /* Data Size ( entre 1 et 255 ) */
793    size = strlen((char *)comm);
794    c[0] = (unsigned char)size;
795    (void)fwrite(c, sizeof(unsigned char), 1, file);
796 
797    /* Data */
798    (void)fwrite(comm, sizeof(unsigned char), size, file);
799 
800    /* Block Terminator */
801    c[0] = '\0';
802    (void)fwrite(c, sizeof(unsigned char), 1, file);
803 
804 }
805 
806 /******************************************************************************/
807 
dumpToGif_init()808 VisuDump* dumpToGif_init()
809 {
810   VisuDump *gif;
811   const gchar *typeGIF[] = {"*.gif", (char*)0};
812   #define descrGIF _("Gif (256 colors) file")
813 
814   gif = VISU_DUMP(visu_dump_scene_new(descrGIF, typeGIF, writeViewInGifFormat, FALSE));
815 
816   waitData = (gpointer)0;
817   waitFunc = (ToolVoidDataFunc)0;
818 
819   return gif;
820 }
821 
822 
writeViewInGifFormat(ToolFileFormat * format _U_,const char * filename,VisuGlNodeScene * scene,guint width,guint height,GError ** error,ToolVoidDataFunc functionWait,gpointer data)823 static gboolean writeViewInGifFormat(ToolFileFormat *format _U_, const char* filename,
824 				      VisuGlNodeScene *scene, guint width, guint height,
825 				      GError **error, ToolVoidDataFunc functionWait,
826                                       gpointer data)
827 {
828   register guint i;
829   register ColorPacket *q;
830   register unsigned char *p;
831   unsigned char bits_per_pixel, c;
832   guint status;
833   GArray *imageData;
834 
835   g_return_val_if_fail(error && !*error, FALSE);
836 
837   imageData = visu_gl_ext_set_getPixmapData(VISU_GL_EXT_SET(scene),
838                                             width, height, FALSE);
839   if (!imageData)
840     {
841       *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_OPENGL,
842                            _("Can't dump OpenGL area to data.\n"));
843       return FALSE;
844     }
845 
846   waitData   = data;
847   waitFunc   = functionWait;
848   image      = (unsigned char*)imageData->data;
849 
850   DBG_fprintf(stderr, "Dump Gif : begin export in %dx%d...\n", width, height);
851 
852   file = fopen(filename, "wb");
853   if(!file)
854     {
855       *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_FILE,
856 			   _("Cannot open file (to write in)."));
857       return FALSE;
858     }
859 
860   img=g_malloc(sizeof(Image));
861   img->colormap = (ColorPacket*)0;
862 
863   img->columns = width;
864   img->rows = height;
865   img->packets=img->columns*img->rows;
866   img->pixels=g_malloc(img->packets*sizeof(ColorPacket));
867   q=img->pixels;
868   p=image;
869   for (i=0; i < img->packets; i++) {
870     q->red=(*p++);
871     q->green=(*p++);
872     q->blue=(*p++);
873     q->index=0;
874     q++;
875   }
876 
877   if(dumpToGif_quantizeImage(256, error, waitFunc, data))
878     {
879       g_free(img->pixels);
880       if (img->colormap)
881 	g_free(img->colormap);
882       g_free(img);
883       return FALSE;
884     }
885   dumpToGif_syncImage();
886 
887   for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
888     if ((1 << bits_per_pixel) >= (int)img->colors) break;
889 
890 
891   (void) fwrite("GIF89a",1,6,file);
892   LSBFirstWriteShort(img->columns);
893   LSBFirstWriteShort(img->rows);
894   c=0x80;  /* global colormap */
895   c|=(8-1) << 4;  /* color resolution */
896   c|=(bits_per_pixel-1);   /* size of global colormap */
897   (void) fputc((int)(char) c,file);
898   (void) fputc(0x0,file);  /* background color */
899   (void) fputc(0x0,file);  /* reserved */
900 
901 
902   for (i=0; i < img->colors; i++) {
903     (void) fputc((int)(char) img->colormap[i].red,file);
904     (void) fputc((int)(char) img->colormap[i].green,file);
905     (void) fputc((int)(char) img->colormap[i].blue,file);
906   }
907   for ( ; i < (guint)(1 << bits_per_pixel) ; i++) {
908     (void) fputc(0x0,file);
909     (void) fputc(0x0,file);
910     (void) fputc(0x0,file);
911   }
912   /* écriture du commentaire */
913   write_comment_comm((unsigned char *)"Image créée par le programme V_Sim\n"
914 		     "Auteur : L. BILLARD");
915 
916   (void) fputc(',',file);  /* image separator */
917 
918 
919   LSBFirstWriteShort(0);
920   LSBFirstWriteShort(0);
921   LSBFirstWriteShort(img->columns);
922   LSBFirstWriteShort(img->rows);
923   (void) fputc(0x0,file);
924   c=Max(bits_per_pixel,2);
925   (void) fputc((int)(char) c,file);
926   status=LZWEncodeImage(Max(bits_per_pixel,2)+1);
927   if (status == False)
928     {
929       *error = g_error_new(VISU_DUMP_ERROR, DUMP_ERROR_ENCODE,
930 			   _("Fail to compress the GIF file."));
931       g_free(img->pixels);
932       g_free(img->colormap);
933       g_free(img);
934       return FALSE;
935   }
936   (void) fputc(0x0,file);
937   (void) fputc(';',file); /* terminator */
938 
939   (void) fclose(file);
940 
941   g_free(img->pixels);
942   g_free(img->colormap);
943   g_free(img);
944 
945   g_array_free(imageData, TRUE);
946 
947   return TRUE;
948 }
949