1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /* Grafx2 - The Ultimate 256-color bitmap paint program
4
5 Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6
7 Grafx2 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2
10 of the License.
11
12 Grafx2 is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20
21 ///@file giformat.c
22 /// Saving and loading GIF
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include "struct.h"
27 #include "global.h"
28 #include "oldies.h"
29 #include "io.h"
30 #include "loadsave.h"
31 #include "loadsavefuncs.h"
32 #include "gfx2mem.h"
33 #include "gfx2log.h"
34
35 #ifndef MIN
36 #define MIN(a,b) ((a)<(b)?(a):(b))
37 #endif
38 #ifndef MAX
39 #define MAX(a,b) ((a)>(b)?(a):(b))
40 #endif
41
42 /**
43 * @defgroup GIF GIF format
44 * @ingroup loadsaveformats
45 * Graphics Interchange Format
46 *
47 * The GIF format uses LZW compression and stores indexed color pictures
48 * up to 256 colors. It has the ability to store several pictures in the same
49 * file : GrafX2 takes advantage of this feature for storing layered images
50 * and animations.
51 *
52 * GrafX2 implements GIF89a :
53 * https://www.w3.org/Graphics/GIF/spec-gif89a.txt
54 *
55 * @{
56 */
57
58 /// Logical Screen Descriptor Block
59 typedef struct
60 {
61 word Width; ///< Width of the complete image area
62 word Height; ///< Height of the complete image area
63 byte Resol; ///< Informations about the resolution (and other)
64 byte Backcol; ///< Proposed background color
65 byte Aspect; ///< Informations about aspect ratio. Ratio = (Aspect + 15) / 64
66 } T_GIF_LSDB;
67
68 /// Image Descriptor Block
69 typedef struct
70 {
71 word Pos_X; ///< X offset where the image should be pasted
72 word Pos_Y; ///< Y offset where the image should be pasted
73 word Image_width; ///< Width of image
74 word Image_height; ///< Height of image
75 byte Indicator; ///< Misc image information
76 byte Nb_bits_pixel; ///< Nb de bits par pixel
77 } T_GIF_IDB;
78
79 /// Graphic Control Extension
80 typedef struct
81 {
82 byte Block_identifier; ///< 0x21
83 byte Function; ///< 0xF9
84 byte Block_size; ///< 4
85 byte Packed_fields; ///< 11100000 : Reserved
86 ///< 00011100 : Disposal method
87 ///< 00000010 : User input flag
88 ///< 00000001 : Transparent flag
89 word Delay_time; ///< Time for this frame to stay displayed
90 byte Transparent_color; ///< Which color index acts as transparent
91 word Block_terminator; ///< 0x00
92 } T_GIF_GCE;
93
94 enum DISPOSAL_METHOD
95 {
96 DISPOSAL_METHOD_UNDEFINED = 0,
97 DISPOSAL_METHOD_DO_NOT_DISPOSE = 1,
98 DISPOSAL_METHOD_RESTORE_BGCOLOR = 2,
99 DISPOSAL_METHOD_RESTORE_PREVIOUS = 3,
100 };
101
102
103 /// Test if a file is GIF format
Test_GIF(T_IO_Context * context,FILE * file)104 void Test_GIF(T_IO_Context * context, FILE * file)
105 {
106 char signature[6];
107
108 (void)context;
109 File_error=1;
110
111 if (Read_bytes(file,signature,6))
112 {
113 /// checks if the signature (6 first bytes) is either GIF87a or GIF89a
114 if ((!memcmp(signature,"GIF87a",6))||(!memcmp(signature,"GIF89a",6)))
115 File_error=0;
116 }
117 }
118
119
120 // -- Lire un fichier au format GIF -----------------------------------------
121
122 typedef struct {
123 word nb_bits; ///< bits for a code
124 word remainder_bits; ///< available bits in @ref last_byte field
125 byte remainder_byte; ///< Remaining bytes in current block
126 word current_code; ///< current code (generally the one just read)
127 byte last_byte; ///< buffer byte for reading bits for codes
128 word pos_X; ///< Current coordinates
129 word pos_Y;
130 word interlaced; ///< interlaced flag
131 word pass; ///< current pass in interlaced decoding
132 word stop; ///< Stop flag (end of picture)
133 } T_GIF_context;
134
135
136 /// Reads the next code (GIF.nb_bits bits)
GIF_get_next_code(FILE * GIF_file,T_GIF_context * gif)137 static word GIF_get_next_code(FILE * GIF_file, T_GIF_context * gif)
138 {
139 word nb_bits_to_process = gif->nb_bits;
140 word nb_bits_processed = 0;
141 word current_nb_bits;
142
143 gif->current_code = 0;
144
145 while (nb_bits_to_process)
146 {
147 if (gif->remainder_bits == 0) // Il ne reste plus de bits...
148 {
149 // Lire l'octet suivant:
150
151 // Si on a atteint la fin du bloc de Raster Data
152 if (gif->remainder_byte == 0)
153 {
154 // Lire l'octet nous donnant la taille du bloc de Raster Data suivant
155 if(Read_byte(GIF_file, &gif->remainder_byte)!=1)
156 {
157 File_error=2;
158 return 0;
159 }
160 if (gif->remainder_byte == 0) // still nothing ? That is the end data block
161 {
162 File_error = 2;
163 GFX2_Log(GFX2_WARNING, "GIF 0 sized data block\n");
164 return gif->current_code;
165 }
166 }
167 if(Read_byte(GIF_file,&gif->last_byte)!=1)
168 {
169 File_error = 2;
170 GFX2_Log(GFX2_ERROR, "GIF failed to load data byte\n");
171 return 0;
172 }
173 gif->remainder_byte--;
174 gif->remainder_bits=8;
175 }
176
177 current_nb_bits=(nb_bits_to_process<=gif->remainder_bits)?nb_bits_to_process:gif->remainder_bits;
178
179 gif->current_code |= (gif->last_byte & ((1<<current_nb_bits)-1))<<nb_bits_processed;
180 gif->last_byte >>= current_nb_bits;
181 nb_bits_processed += current_nb_bits;
182 nb_bits_to_process -= current_nb_bits;
183 gif->remainder_bits -= current_nb_bits;
184 }
185
186 return gif->current_code;
187 }
188
189 /// Put a new pixel
GIF_new_pixel(T_IO_Context * context,T_GIF_context * gif,T_GIF_IDB * idb,int is_transparent,byte color)190 static void GIF_new_pixel(T_IO_Context * context, T_GIF_context * gif, T_GIF_IDB *idb, int is_transparent, byte color)
191 {
192 if (!is_transparent || color!=context->Transparent_color)
193 Set_pixel(context, idb->Pos_X+gif->pos_X, idb->Pos_Y+gif->pos_Y,color);
194
195 gif->pos_X++;
196
197 if (gif->pos_X >= idb->Image_width)
198 {
199 gif->pos_X=0;
200
201 if (!gif->interlaced)
202 {
203 gif->pos_Y++;
204 if (gif->pos_Y >= idb->Image_height)
205 gif->stop = 1;
206 }
207 else
208 {
209 switch (gif->pass)
210 {
211 case 0 :
212 case 1 : gif->pos_Y+=8;
213 break;
214 case 2 : gif->pos_Y+=4;
215 break;
216 default: gif->pos_Y+=2;
217 }
218
219 if (gif->pos_Y >= idb->Image_height)
220 {
221 switch(++(gif->pass))
222 {
223 case 1 : gif->pos_Y=4;
224 break;
225 case 2 : gif->pos_Y=2;
226 break;
227 case 3 : gif->pos_Y=1;
228 break;
229 case 4 : gif->stop = 1;
230 }
231 }
232 }
233 }
234 }
235
236
237 /// Load GIF file
Load_GIF(T_IO_Context * context)238 void Load_GIF(T_IO_Context * context)
239 {
240 FILE *GIF_file;
241 int image_mode = -1;
242 char signature[6];
243
244 word * alphabet_stack; // Pile de décodage d'une chaîne
245 word * alphabet_prefix; // Table des préfixes des codes
246 word * alphabet_suffix; // Table des suffixes des codes
247 word alphabet_free; // Position libre dans l'alphabet
248 word alphabet_max; // Nombre d'entrées possibles dans l'alphabet
249 word alphabet_stack_pos; // Position dans la pile de décodage d'un chaîne
250
251 T_GIF_context GIF;
252 T_GIF_LSDB LSDB;
253 T_GIF_IDB IDB;
254 T_GIF_GCE GCE;
255
256 word nb_colors; // Nombre de couleurs dans l'image
257 word color_index; // index de traitement d'une couleur
258 byte size_to_read; // Nombre de données à lire (divers)
259 byte block_identifier; // Code indicateur du type de bloc en cours
260 byte initial_nb_bits; // Nb de bits au début du traitement LZW
261 word special_case=0; // Mémoire pour le cas spécial
262 word old_code=0; // Code précédent
263 word byte_read; // Sauvegarde du code en cours de lecture
264 word value_clr; // Valeur <=> Clear tables
265 word value_eof; // Valeur <=> End d'image
266 long file_size;
267 int number_LID; // Nombre d'images trouvées dans le fichier
268 int current_layer = 0;
269 int last_delay = 0;
270 byte is_transparent = 0;
271 byte is_looping=0;
272 enum PIXEL_RATIO ratio;
273 byte disposal_method = DISPOSAL_METHOD_RESTORE_BGCOLOR;
274
275 byte previous_disposal_method = DISPOSAL_METHOD_RESTORE_BGCOLOR;
276 word previous_width=0;
277 word previous_height=0;
278 word previous_pos_x=0;
279 word previous_pos_y=0;
280
281 /////////////////////////////////////////////////// FIN DES DECLARATIONS //
282
283
284 number_LID=0;
285
286 if ((GIF_file=Open_file_read(context)))
287 {
288 file_size=File_length_file(GIF_file);
289 if ( (Read_bytes(GIF_file,signature,6)) &&
290 ( (memcmp(signature,"GIF87a",6)==0) ||
291 (memcmp(signature,"GIF89a",6)==0) ) )
292 {
293
294 // Allocation de mémoire pour les tables & piles de traitement:
295 alphabet_stack = (word *)GFX2_malloc(4096*sizeof(word));
296 alphabet_prefix = (word *)GFX2_malloc(4096*sizeof(word));
297 alphabet_suffix = (word *)GFX2_malloc(4096*sizeof(word));
298
299 if (Read_word_le(GIF_file,&(LSDB.Width))
300 && Read_word_le(GIF_file,&(LSDB.Height))
301 && Read_byte(GIF_file,&(LSDB.Resol))
302 && Read_byte(GIF_file,&(LSDB.Backcol))
303 && Read_byte(GIF_file,&(LSDB.Aspect))
304 )
305 {
306 // Lecture du Logical Screen Descriptor Block réussie:
307
308 Original_screen_X=LSDB.Width;
309 Original_screen_Y=LSDB.Height;
310
311 ratio=PIXEL_SIMPLE; // (49 + 15) / 64 = 1:1
312 if (LSDB.Aspect != 0) {
313 if (LSDB.Aspect < 25) // (17 + 15) / 64 = 1:2
314 ratio=PIXEL_TALL;
315 else if (LSDB.Aspect < 41) // (33 + 15) / 64 = 3:4
316 ratio=PIXEL_TALL3;
317 else if (LSDB.Aspect > 82) // (113 + 15) / 64 = 2:1
318 ratio=PIXEL_WIDE;
319 }
320
321 Pre_load(context, LSDB.Width,LSDB.Height,file_size,FORMAT_GIF,ratio,(LSDB.Resol&7)+1);
322
323 // Palette globale dispo = (LSDB.Resol and $80)
324 // Profondeur de couleur =((LSDB.Resol and $70) shr 4)+1
325 // Nombre de bits/pixel = (LSDB.Resol and $07)+1
326 // Ordre de Classement = (LSDB.Aspect and $80)
327
328 nb_colors=(1 << ((LSDB.Resol & 0x07)+1));
329 if (LSDB.Resol & 0x80)
330 {
331 // Palette globale dispo:
332
333 if (Config.Clear_palette)
334 memset(context->Palette,0,sizeof(T_Palette));
335
336 // Load the palette
337 for(color_index=0;color_index<nb_colors;color_index++)
338 {
339 Read_byte(GIF_file,&(context->Palette[color_index].R));
340 Read_byte(GIF_file,&(context->Palette[color_index].G));
341 Read_byte(GIF_file,&(context->Palette[color_index].B));
342 }
343 }
344
345 // On lit un indicateur de block
346 Read_byte(GIF_file,&block_identifier);
347 while (block_identifier!=0x3B && !File_error)
348 {
349 switch (block_identifier)
350 {
351 case 0x21: // Bloc d'extension
352 {
353 byte function_code;
354 // Lecture du code de fonction:
355 Read_byte(GIF_file,&function_code);
356 // Lecture de la taille du bloc:
357 Read_byte(GIF_file,&size_to_read);
358 while (size_to_read!=0 && !File_error)
359 {
360 switch(function_code)
361 {
362 case 0xFE: // Comment Block Extension
363 // On récupère le premier commentaire non-vide,
364 // on jette les autres.
365 if (context->Comment[0]=='\0')
366 {
367 int nb_char_to_keep = MIN(size_to_read, COMMENT_SIZE);
368
369 Read_bytes(GIF_file,context->Comment,nb_char_to_keep);
370 context->Comment[nb_char_to_keep+1]='\0';
371 // Si le commentaire etait trop long, on fait avance-rapide
372 // sur la suite.
373 if (size_to_read>nb_char_to_keep)
374 fseek(GIF_file,size_to_read-nb_char_to_keep,SEEK_CUR);
375 }
376 // Lecture de la taille du bloc suivant:
377 Read_byte(GIF_file,&size_to_read);
378 break;
379 case 0xF9: // Graphics Control Extension
380 // Prévu pour la transparence
381 if ( Read_byte(GIF_file,&(GCE.Packed_fields))
382 && Read_word_le(GIF_file,&(GCE.Delay_time))
383 && Read_byte(GIF_file,&(GCE.Transparent_color)))
384 {
385 previous_disposal_method = disposal_method;
386 disposal_method = (GCE.Packed_fields >> 2) & 7;
387 last_delay = GCE.Delay_time;
388 context->Transparent_color= GCE.Transparent_color;
389 is_transparent = GCE.Packed_fields & 1;
390 GFX2_Log(GFX2_DEBUG, "GIF Graphics Control Extension : transp=%d (color #%u) delay=%ums disposal_method=%d\n", is_transparent, GCE.Transparent_color, 10*GCE.Delay_time, disposal_method);
391 if (number_LID == 0)
392 context->Background_transparent = is_transparent;
393 is_transparent &= is_looping;
394 }
395 else
396 File_error=2;
397 // Lecture de la taille du bloc suivant:
398 Read_byte(GIF_file,&size_to_read);
399 break;
400
401 case 0xFF: // Application Extension
402 // Normally, always a 11-byte block
403 if (size_to_read == 0x0B)
404 {
405 char aeb[0x0B];
406 Read_bytes(GIF_file,aeb, 0x0B);
407 GFX2_Log(GFX2_DEBUG, "GIF extension \"%.11s\"\n", aeb);
408 if (File_error)
409 ;
410 else if (!memcmp(aeb,"NETSCAPE2.0",0x0B))
411 {
412 is_looping=1;
413 // The well-known Netscape extension.
414 // Load as an animation
415 Set_image_mode(context, IMAGE_MODE_ANIMATION);
416 // Skip sub-block
417 do
418 {
419 if (! Read_byte(GIF_file,&size_to_read))
420 File_error=1;
421 fseek(GIF_file,size_to_read,SEEK_CUR);
422 } while (!File_error && size_to_read!=0);
423 }
424 else if (!memcmp(aeb,"GFX2PATH\x00\x00\x00",0x0B))
425 {
426 // Original file path
427 Read_byte(GIF_file,&size_to_read);
428 if (!File_error && size_to_read > 0)
429 {
430 free(context->Original_file_directory);
431 context->Original_file_directory = GFX2_malloc(size_to_read);
432 Read_bytes(GIF_file, context->Original_file_directory, size_to_read);
433 Read_byte(GIF_file, &size_to_read);
434 if (!File_error && size_to_read > 0)
435 {
436 free(context->Original_file_name);
437 context->Original_file_name = GFX2_malloc(size_to_read);
438 Read_bytes(GIF_file, context->Original_file_name, size_to_read);
439 Read_byte(GIF_file, &size_to_read); // Normally 0
440 }
441 }
442 }
443 else if (!memcmp(aeb,"CRNG\0\0\0\0" "1.0",0x0B))
444 {
445 // Color animation. Similar to a CRNG chunk in IFF file format.
446 word rate;
447 word flags;
448 byte col1;
449 byte col2;
450 //
451 Read_byte(GIF_file,&size_to_read);
452 for(;size_to_read>0 && !File_error;size_to_read-=6)
453 {
454 if ( (Read_word_be(GIF_file, &rate))
455 && (Read_word_be(GIF_file, &flags))
456 && (Read_byte(GIF_file, &col1))
457 && (Read_byte(GIF_file, &col2)))
458 {
459 if (col1 != col2)
460 {
461 // Valid cycling range
462 context->Cycle_range[context->Color_cycles].Start = MIN(col1, col2);
463 context->Cycle_range[context->Color_cycles].End = MAX(col1, col2);
464 context->Cycle_range[context->Color_cycles].Inverse = (flags&2)?1:0;
465 context->Cycle_range[context->Color_cycles].Speed = (flags&1)?rate/78:0;
466
467 context->Color_cycles++;
468 }
469 }
470 else
471 {
472 File_error=1;
473 }
474 }
475 // Read end-of-block delimiter
476 if (!File_error)
477 Read_byte(GIF_file,&size_to_read);
478 if (size_to_read!=0)
479 File_error=1;
480 }
481 else if (0 == memcmp(aeb, "GFX2MODE", 8))
482 {
483 Read_byte(GIF_file,&size_to_read);
484 if (size_to_read > 0)
485 { // read the image mode. We'll set it after having loaded all layers.
486 char * label = GFX2_malloc((size_t)size_to_read + 1);
487 Read_bytes(GIF_file, label, size_to_read);
488 label[size_to_read] = '\0';
489 image_mode = Constraint_mode_from_label(label);
490 GFX2_Log(GFX2_DEBUG, " mode = %s (%d)\n", label, image_mode);
491 free(label);
492 Read_byte(GIF_file,&size_to_read);
493 // be future proof, skip following sub-blocks :
494 while (size_to_read!=0 && !File_error)
495 {
496 if (fseek(GIF_file,size_to_read,SEEK_CUR) < 0)
497 File_error = 1;
498 if (!Read_byte(GIF_file,&size_to_read))
499 File_error = 1;
500 }
501 }
502 }
503 else
504 {
505 // Unknown extension, skip.
506 Read_byte(GIF_file,&size_to_read);
507 while (size_to_read!=0 && !File_error)
508 {
509 if (fseek(GIF_file,size_to_read,SEEK_CUR) < 0)
510 File_error = 1;
511 if (!Read_byte(GIF_file,&size_to_read))
512 File_error = 1;
513 }
514 }
515 }
516 else
517 {
518 fseek(GIF_file,size_to_read,SEEK_CUR);
519 // Lecture de la taille du bloc suivant:
520 Read_byte(GIF_file,&size_to_read);
521 }
522 break;
523
524 default:
525 // On saute le bloc:
526 fseek(GIF_file,size_to_read,SEEK_CUR);
527 // Lecture de la taille du bloc suivant:
528 Read_byte(GIF_file,&size_to_read);
529 break;
530 }
531 }
532 }
533 break;
534 case 0x2C: // Local Image Descriptor
535 {
536 if (number_LID!=0)
537 {
538 // This a second layer/frame, or more.
539 // Attempt to add a layer to current image
540 current_layer++;
541 Set_loading_layer(context, current_layer);
542 if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
543 {
544 // Copy the content of previous layer.
545 memcpy(
546 Main.backups->Pages->Image[Main.current_layer].Pixels,
547 Main.backups->Pages->Image[Main.current_layer-1].Pixels,
548 Main.backups->Pages->Width*Main.backups->Pages->Height);
549 }
550 else
551 {
552 Fill_canvas(context, is_transparent ? context->Transparent_color : LSDB.Backcol);
553 }
554 }
555 else
556 {
557 // First frame/layer, fill canvas with backcolor
558 Fill_canvas(context, is_transparent ? context->Transparent_color : LSDB.Backcol);
559 }
560 // Duration was set in the previously loaded GCE
561 Set_frame_duration(context, last_delay*10);
562 number_LID++;
563
564 // lecture de 10 derniers octets
565 if ( Read_word_le(GIF_file,&(IDB.Pos_X))
566 && Read_word_le(GIF_file,&(IDB.Pos_Y))
567 && Read_word_le(GIF_file,&(IDB.Image_width))
568 && Read_word_le(GIF_file,&(IDB.Image_height))
569 && Read_byte(GIF_file,&(IDB.Indicator))
570 && IDB.Image_width && IDB.Image_height)
571 {
572 GFX2_Log(GFX2_DEBUG, "GIF Image descriptor %u Pos (%u,%u) %ux%u %s%slocal palette(%ubpp)\n",
573 number_LID, IDB.Pos_X, IDB.Pos_Y, IDB.Image_width, IDB.Image_height,
574 (IDB.Indicator & 0x40) ? "interlaced " : "", (IDB.Indicator & 0x80) ? "" : "no ",
575 (IDB.Indicator & 7) + 1);
576 // Palette locale dispo = (IDB.Indicator and $80)
577 // Image entrelacée = (IDB.Indicator and $40)
578 // Ordre de classement = (IDB.Indicator and $20)
579 // Nombre de bits/pixel = (IDB.Indicator and $07)+1 (si palette locale dispo)
580
581 if (IDB.Indicator & 0x80)
582 {
583 // Palette locale dispo
584
585 if (Config.Clear_palette)
586 memset(context->Palette,0,sizeof(T_Palette));
587
588 nb_colors=(1 << ((IDB.Indicator & 0x07)+1));
589 // Load the palette
590 for(color_index=0;color_index<nb_colors;color_index++)
591 {
592 Read_byte(GIF_file,&(context->Palette[color_index].R));
593 Read_byte(GIF_file,&(context->Palette[color_index].G));
594 Read_byte(GIF_file,&(context->Palette[color_index].B));
595 }
596
597 }
598 if (number_LID!=1)
599 {
600 // This a second layer/frame, or more.
601 if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
602 {
603 // Need to clear previous image to back-color.
604 if (previous_disposal_method==DISPOSAL_METHOD_RESTORE_BGCOLOR)
605 {
606 int y;
607 for (y=0; y<previous_height; y++)
608 memset(
609 Main.backups->Pages->Image[Main.current_layer].Pixels
610 + (previous_pos_y+y)* Main.backups->Pages->Width+previous_pos_x,
611 is_transparent ? context->Transparent_color : LSDB.Backcol,
612 previous_width);
613 }
614 }
615 }
616 previous_height=IDB.Image_height;
617 previous_width=IDB.Image_width;
618 previous_pos_x=IDB.Pos_X;
619 previous_pos_y=IDB.Pos_Y;
620
621 File_error=0;
622 if (!Read_byte(GIF_file,&(initial_nb_bits)))
623 File_error=1;
624
625 value_clr =(1<<initial_nb_bits)+0;
626 value_eof =(1<<initial_nb_bits)+1;
627 alphabet_free=(1<<initial_nb_bits)+2;
628
629 GIF.nb_bits =initial_nb_bits + 1;
630 alphabet_max =((1 << GIF.nb_bits)-1);
631 GIF.interlaced =(IDB.Indicator & 0x40);
632 GIF.pass =0;
633
634 /*Init_lecture();*/
635
636
637 GIF.stop = 0;
638
639 //////////////////////////////////////////// DECOMPRESSION LZW //
640
641 GIF.pos_X=0;
642 GIF.pos_Y=0;
643 alphabet_stack_pos=0;
644 GIF.last_byte =0;
645 GIF.remainder_bits =0;
646 GIF.remainder_byte =0;
647
648 while ( (GIF_get_next_code(GIF_file, &GIF)!=value_eof) && (!File_error) )
649 {
650 if (GIF.current_code > alphabet_free)
651 {
652 GFX2_Log(GFX2_INFO, "Load_GIF() Invalid code %u (should be <=%u)\n", GIF.current_code, alphabet_free);
653 File_error=2;
654 break;
655 }
656 else if (GIF.current_code != value_clr)
657 {
658 byte_read = GIF.current_code;
659 if (alphabet_free == GIF.current_code)
660 {
661 GIF.current_code=old_code;
662 alphabet_stack[alphabet_stack_pos++]=special_case;
663 }
664
665 while (GIF.current_code > value_clr)
666 {
667 if (GIF.current_code >= 4096)
668 {
669 GFX2_Log(GFX2_ERROR, "Load_GIF() GIF.current_code = %u >= 4096\n", GIF.current_code);
670 File_error = 2;
671 break;
672 }
673 alphabet_stack[alphabet_stack_pos++] = alphabet_suffix[GIF.current_code];
674 GIF.current_code = alphabet_prefix[GIF.current_code];
675 }
676
677 special_case = alphabet_stack[alphabet_stack_pos++] = GIF.current_code;
678
679 do
680 GIF_new_pixel(context, &GIF, &IDB, is_transparent, alphabet_stack[--alphabet_stack_pos]);
681 while (alphabet_stack_pos!=0);
682
683 alphabet_prefix[alphabet_free ]=old_code;
684 alphabet_suffix[alphabet_free++]=GIF.current_code;
685 old_code=byte_read;
686
687 if (alphabet_free>alphabet_max)
688 {
689 if (GIF.nb_bits<12)
690 alphabet_max =((1 << (++GIF.nb_bits))-1);
691 }
692 }
693 else // Clear code
694 {
695 GIF.nb_bits = initial_nb_bits + 1;
696 alphabet_max = ((1 << GIF.nb_bits)-1);
697 alphabet_free = (1<<initial_nb_bits)+2;
698 special_case = GIF_get_next_code(GIF_file, &GIF);
699 if (GIF.current_code >= value_clr)
700 {
701 GFX2_Log(GFX2_INFO, "Load_GIF() Invalid code %u just after clear (=%u)!\n",
702 GIF.current_code, value_clr);
703 File_error = 2;
704 break;
705 }
706 old_code = GIF.current_code;
707 GIF_new_pixel(context, &GIF, &IDB, is_transparent, GIF.current_code);
708 }
709 }
710
711 if (File_error == 2 && GIF.pos_X == 0 && GIF.pos_Y == IDB.Image_height)
712 File_error=0;
713
714 if (File_error >= 0 && !GIF.stop)
715 File_error=2;
716
717 // No need to read more than one frame in animation preview mode
718 if (context->Type == CONTEXT_PREVIEW && is_looping)
719 {
720 goto early_exit;
721 }
722 // Same with brush
723 if (context->Type == CONTEXT_BRUSH && is_looping)
724 {
725 goto early_exit;
726 }
727
728 } // Le fichier contenait un IDB
729 else
730 File_error=2;
731 }
732 default:
733 break;
734 }
735 // Lecture du code de fonction suivant:
736 if (!Read_byte(GIF_file,&block_identifier))
737 File_error=2;
738 }
739
740 // set the mode that have been read previously.
741 if (image_mode > 0)
742 Set_image_mode(context, image_mode);
743 } // Le fichier contenait un LSDB
744 else
745 File_error=1;
746
747 early_exit:
748
749 // Libération de la mémoire utilisée par les tables & piles de traitement:
750 free(alphabet_suffix);
751 free(alphabet_prefix);
752 free(alphabet_stack);
753 alphabet_suffix = alphabet_prefix = alphabet_stack = NULL;
754 } // Le fichier contenait au moins la signature GIF87a ou GIF89a
755 else
756 File_error=1;
757
758 fclose(GIF_file);
759
760 } // Le fichier était ouvrable
761 else
762 File_error=1;
763 }
764
765
766 // -- Sauver un fichier au format GIF ---------------------------------------
767
768 /// Flush the buffer
GIF_empty_buffer(FILE * file,T_GIF_context * gif,byte * GIF_buffer)769 static void GIF_empty_buffer(FILE * file, T_GIF_context *gif, byte * GIF_buffer)
770 {
771 if (gif->remainder_byte)
772 {
773 GIF_buffer[0] = gif->remainder_byte;
774
775 if (!Write_bytes(file, GIF_buffer, (size_t)gif->remainder_byte + 1))
776 File_error = 1;
777
778 gif->remainder_byte = 0;
779 }
780 }
781
782 /// Write a code (GIF_nb_bits bits)
GIF_set_code(FILE * GIF_file,T_GIF_context * gif,byte * GIF_buffer,word Code)783 static void GIF_set_code(FILE * GIF_file, T_GIF_context * gif, byte * GIF_buffer, word Code)
784 {
785 word nb_bits_to_process = gif->nb_bits;
786 word nb_bits_processed =0;
787 word current_nb_bits;
788
789 while (nb_bits_to_process)
790 {
791 current_nb_bits = (nb_bits_to_process <= (8-gif->remainder_bits)) ?
792 nb_bits_to_process: (8-gif->remainder_bits);
793
794 gif->last_byte |= (Code & ((1<<current_nb_bits)-1))<<gif->remainder_bits;
795 Code>>=current_nb_bits;
796 gif->remainder_bits +=current_nb_bits;
797 nb_bits_processed +=current_nb_bits;
798 nb_bits_to_process-=current_nb_bits;
799
800 if (gif->remainder_bits==8) // Il ne reste plus de bits à coder sur l'octet courant
801 {
802 // Ecrire l'octet à balancer:
803 GIF_buffer[++(gif->remainder_byte)] = gif->last_byte;
804
805 // Si on a atteint la fin du bloc de Raster Data
806 if (gif->remainder_byte==255)
807 // On doit vider le buffer qui est maintenant plein
808 GIF_empty_buffer(GIF_file, gif, GIF_buffer);
809
810 gif->last_byte=0;
811 gif->remainder_bits=0;
812 }
813 }
814 }
815
816
817 /// Read the next pixel
GIF_next_pixel(T_IO_Context * context,T_GIF_context * gif,T_GIF_IDB * idb)818 static byte GIF_next_pixel(T_IO_Context *context, T_GIF_context *gif, T_GIF_IDB *idb)
819 {
820 byte temp;
821
822 temp = Get_pixel(context, gif->pos_X, gif->pos_Y);
823
824 if (++gif->pos_X >= (idb->Image_width + idb->Pos_X))
825 {
826 gif->pos_X = idb->Pos_X;
827 if (++gif->pos_Y >= (idb->Image_height + idb->Pos_Y))
828 gif->stop = 1;
829 }
830
831 return temp;
832 }
833
834 struct gif_alphabet {
835 word prefix[4096]; // code prefix array
836 word suffix[4096]; // code suffix array
837 word daughter[4096]; // daughter strings array (greater length)
838 word sister[4096]; // sister strings array (same length)
839 word free; // first free slot in the alphabet
840 word max; // maximum number of entry in the alphabet
841 };
842
843 /// Save a GIF file
Save_GIF(T_IO_Context * context)844 void Save_GIF(T_IO_Context * context)
845 {
846 FILE * GIF_file;
847 byte GIF_buffer[256]; // buffer d'écriture de bloc de données compilées
848
849 struct gif_alphabet * alphabet;
850 word start; // Code précédent (sert au linkage des chaînes)
851 int descend; // Booléen "On vient de descendre"
852
853 T_GIF_context GIF;
854 T_GIF_LSDB LSDB;
855 T_GIF_IDB IDB;
856
857
858 byte block_identifier; // Code indicateur du type de bloc en cours
859 word current_string; // Code de la chaîne en cours de traitement
860 byte current_char; // Caractère à coder
861 word index; // index de recherche de chaîne
862 int current_layer;
863
864 word clear; // LZW clear code
865 word eof; // End of image code
866
867 /////////////////////////////////////////////////// FIN DES DECLARATIONS //
868
869 File_error=0;
870
871 if ((GIF_file=Open_file_write(context)))
872 {
873 // On écrit la signature du fichier
874 if (Write_bytes(GIF_file,"GIF89a",6))
875 {
876 // La signature du fichier a été correctement écrite.
877
878 // Allocation de mémoire pour les tables
879 alphabet = (struct gif_alphabet *)GFX2_malloc(sizeof(struct gif_alphabet));
880 if (alphabet == NULL)
881 {
882 File_error = 1;
883 fclose(GIF_file);
884 return;
885 }
886
887 // On initialise le LSDB du fichier
888 if (Config.Screen_size_in_GIF && Screen_width >= context->Width && Screen_height >= context->Height)
889 {
890 // Canvas bigger than the image
891 LSDB.Width=Screen_width;
892 LSDB.Height=Screen_height;
893 }
894 else
895 {
896 LSDB.Width=context->Width;
897 LSDB.Height=context->Height;
898 }
899 LSDB.Resol = 0xF7; // Global palette of 256 entries, 256 color image
900 // 0xF7 = 1111 0111
901 // <Packed Fields> = Global Color Table Flag 1 Bit
902 // Color Resolution 3 Bits
903 // Sort Flag 1 Bit
904 // Size of Global Color Table 3 Bits
905 LSDB.Backcol=context->Transparent_color;
906 switch(context->Ratio)
907 {
908 case PIXEL_TALL:
909 case PIXEL_TALL2:
910 LSDB.Aspect = 17; // 1:2 = 2:4
911 break;
912 case PIXEL_TALL3:
913 LSDB.Aspect = 33; // 3:4
914 break;
915 case PIXEL_WIDE:
916 case PIXEL_WIDE2:
917 LSDB.Aspect = 113; // 2:1 = 4:2
918 break;
919 default:
920 LSDB.Aspect = 0; // undefined, which is most frequent.
921 // 49 would be 1:1 ratio
922 }
923
924 // On sauve le LSDB dans le fichier
925
926 if (Write_word_le(GIF_file,LSDB.Width) &&
927 Write_word_le(GIF_file,LSDB.Height) &&
928 Write_byte(GIF_file,LSDB.Resol) &&
929 Write_byte(GIF_file,LSDB.Backcol) &&
930 Write_byte(GIF_file,LSDB.Aspect) )
931 {
932 // Le LSDB a été correctement écrit.
933 int i;
934 // On sauve la palette
935 for(i=0;i<256 && !File_error;i++)
936 {
937 if (!Write_byte(GIF_file,context->Palette[i].R)
938 ||!Write_byte(GIF_file,context->Palette[i].G)
939 ||!Write_byte(GIF_file,context->Palette[i].B))
940 File_error=1;
941 }
942 if (!File_error)
943 {
944 // La palette a été correctement écrite.
945
946 /// - "Netscape" animation extension :
947 /// <pre>
948 /// 0x21 Extension Label
949 /// 0xFF Application Extension Label
950 /// 0x0B Block Size
951 /// "NETSCAPE" Application Identifier (8 bytes)
952 /// "2.0" Application Authentication Code (3 bytes)
953 /// 0x03 Sub-block Data Size
954 /// 0xLL 01 to loop
955 /// 0xSSSS (little endian) number of loops, 0 means infinite loop
956 /// 0x00 Block terminator </pre>
957 /// see http://www.vurdalakov.net/misc/gif/netscape-looping-application-extension
958 if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
959 {
960 if (context->Nb_layers>1)
961 Write_bytes(GIF_file,"\x21\xFF\x0BNETSCAPE2.0\x03\x01\x00\x00\x00",19);
962 }
963 else if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode > IMAGE_MODE_ANIMATION)
964 {
965 /// - GrafX2 extension to store ::IMAGE_MODES :
966 /// <pre>
967 /// 0x21 Extension Label
968 /// 0xFF Application Extension Label
969 /// 0x0B Block Size
970 /// "GFX2MODE" Application Identifier (8 bytes)
971 /// "2.6" Application Authentication Code (3 bytes)
972 /// 0xll Sub-block Data Size
973 /// string label
974 /// 0x00 Block terminator </pre>
975 /// @see Constraint_mode_label()
976 const char * label = Constraint_mode_label(Main.backups->Pages->Image_mode);
977 if (label != NULL)
978 {
979 size_t len = strlen(label);
980 // Write extension for storing IMAGE_MODE
981 Write_byte(GIF_file,0x21); // Extension Introducer
982 Write_byte(GIF_file,0xff); // Extension Label
983 Write_byte(GIF_file, 11); // Block size
984 Write_bytes(GIF_file, "GFX2MODE2.6", 11); // Application Identifier + Appl. Authentication Code
985 Write_byte(GIF_file, (byte)len); // Block size
986 Write_bytes(GIF_file, label, len); // Data
987 Write_byte(GIF_file, 0); // Block terminator
988 }
989 }
990
991 // Ecriture du commentaire
992 if (context->Comment[0])
993 {
994 Write_bytes(GIF_file,"\x21\xFE",2);
995 Write_byte(GIF_file, (byte)strlen(context->Comment));
996 Write_bytes(GIF_file,context->Comment,strlen(context->Comment)+1);
997 }
998 /// - "CRNG" Color cycing extension :
999 /// <pre>
1000 /// 0x21 Extension Label
1001 /// 0xFF Application Extension Label
1002 /// 0x0B Block Size
1003 /// "CRNG\0\0\0\0" "CRNG" Application Identifier (8 bytes)
1004 /// "1.0" Application Authentication Code (3 bytes)
1005 /// 0xll Sub-block Data Size (6 bytes per color cycle)
1006 /// For each color cycle :
1007 /// 0xRRRR (big endian) Rate
1008 /// 0xFFFF (big endian) Flags
1009 /// 0xSS start (lower color index)
1010 /// 0xEE end (higher color index)
1011 /// 0x00 Block terminator </pre>
1012 if (context->Color_cycles)
1013 {
1014 int i;
1015
1016 Write_bytes(GIF_file,"\x21\xff\x0B" "CRNG\0\0\0\0" "1.0",14);
1017 Write_byte(GIF_file,context->Color_cycles*6);
1018 for (i=0; i<context->Color_cycles; i++)
1019 {
1020 word flags=0;
1021 flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not
1022 flags|= context->Cycle_range[i].Inverse?2:0; // Inverted
1023
1024 Write_word_be(GIF_file,context->Cycle_range[i].Speed*78); // Rate
1025 Write_word_be(GIF_file,flags); // Flags
1026 Write_byte(GIF_file,context->Cycle_range[i].Start); // Min color
1027 Write_byte(GIF_file,context->Cycle_range[i].End); // Max color
1028 }
1029 Write_byte(GIF_file,0);
1030 }
1031
1032 // Loop on all layers
1033 for (current_layer=0;
1034 current_layer < context->Nb_layers && !File_error;
1035 current_layer++)
1036 {
1037 // Write a Graphic Control Extension
1038 T_GIF_GCE GCE;
1039 byte disposal_method;
1040
1041 Set_saving_layer(context, current_layer);
1042
1043 GCE.Block_identifier = 0x21;
1044 GCE.Function = 0xF9;
1045 GCE.Block_size=4;
1046
1047 if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
1048 {
1049 // Animation frame
1050 int duration;
1051 if(context->Background_transparent)
1052 disposal_method = DISPOSAL_METHOD_RESTORE_BGCOLOR;
1053 else
1054 disposal_method = DISPOSAL_METHOD_DO_NOT_DISPOSE;
1055 GCE.Packed_fields=(disposal_method<<2)|(context->Background_transparent);
1056 duration=Get_frame_duration(context)/10;
1057 GCE.Delay_time=duration<0xFFFF?duration:0xFFFF;
1058 }
1059 else
1060 {
1061 // Layered image or brush
1062 disposal_method = DISPOSAL_METHOD_DO_NOT_DISPOSE;
1063 if (current_layer==0)
1064 GCE.Packed_fields=(disposal_method<<2)|(context->Background_transparent);
1065 else
1066 GCE.Packed_fields=(disposal_method<<2)|(1);
1067 GCE.Delay_time=5; // Duration 5/100s (minimum viable value for current web browsers)
1068 if (current_layer == context->Nb_layers -1)
1069 GCE.Delay_time=0xFFFF; // Infinity (10 minutes)
1070 }
1071 GCE.Transparent_color=context->Transparent_color;
1072 GCE.Block_terminator=0x00;
1073
1074 if (Write_byte(GIF_file,GCE.Block_identifier)
1075 && Write_byte(GIF_file,GCE.Function)
1076 && Write_byte(GIF_file,GCE.Block_size)
1077 && Write_byte(GIF_file,GCE.Packed_fields)
1078 && Write_word_le(GIF_file,GCE.Delay_time)
1079 && Write_byte(GIF_file,GCE.Transparent_color)
1080 && Write_byte(GIF_file,GCE.Block_terminator)
1081 )
1082 {
1083 byte temp, max = 0;
1084
1085 IDB.Pos_X=0;
1086 IDB.Pos_Y=0;
1087 IDB.Image_width=context->Width;
1088 IDB.Image_height=context->Height;
1089 if(current_layer > 0)
1090 {
1091 word min_X, max_X, min_Y, max_Y;
1092 // find bounding box of changes for Animated GIFs
1093 min_X = min_Y = 0xffff;
1094 max_X = max_Y = 0;
1095 for(GIF.pos_Y = 0; GIF.pos_Y < context->Height; GIF.pos_Y++) {
1096 for(GIF.pos_X = 0; GIF.pos_X < context->Width; GIF.pos_X++) {
1097 if (GIF.pos_X >= min_X && GIF.pos_X <= max_X && GIF.pos_Y >= min_Y && GIF.pos_Y <= max_Y)
1098 continue; // already in the box
1099 if(disposal_method == DISPOSAL_METHOD_DO_NOT_DISPOSE)
1100 {
1101 // if that pixel has same value in previous layer, no need to save it
1102 Set_saving_layer(context, current_layer - 1);
1103 temp = Get_pixel(context, GIF.pos_X, GIF.pos_Y);
1104 Set_saving_layer(context, current_layer);
1105 if(temp == Get_pixel(context, GIF.pos_X, GIF.pos_Y))
1106 continue;
1107 }
1108 if (disposal_method == DISPOSAL_METHOD_RESTORE_BGCOLOR
1109 || context->Background_transparent
1110 || Main.backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
1111 {
1112 // if that pixel is Backcol, no need to save it
1113 if (LSDB.Backcol == Get_pixel(context, GIF.pos_X, GIF.pos_Y))
1114 continue;
1115 }
1116 if(GIF.pos_X < min_X) min_X = GIF.pos_X;
1117 if(GIF.pos_X > max_X) max_X = GIF.pos_X;
1118 if(GIF.pos_Y < min_Y) min_Y = GIF.pos_Y;
1119 if(GIF.pos_Y > max_Y) max_Y = GIF.pos_Y;
1120 }
1121 }
1122 if((min_X <= max_X) && (min_Y <= max_Y))
1123 {
1124 IDB.Pos_X = min_X;
1125 IDB.Pos_Y = min_Y;
1126 IDB.Image_width = max_X + 1 - min_X;
1127 IDB.Image_height = max_Y + 1 - min_Y;
1128 }
1129 else
1130 {
1131 // if no pixel changes, store a 1 pixel image
1132 IDB.Image_width = 1;
1133 IDB.Image_height = 1;
1134 }
1135 }
1136
1137 // look for the maximum pixel value
1138 // to decide how many bit per pixel are needed.
1139 for(GIF.pos_Y = IDB.Pos_Y; GIF.pos_Y < IDB.Image_height + IDB.Pos_Y; GIF.pos_Y++) {
1140 for(GIF.pos_X = IDB.Pos_X; GIF.pos_X < IDB.Image_width + IDB.Pos_X; GIF.pos_X++) {
1141 temp=Get_pixel(context, GIF.pos_X, GIF.pos_Y);
1142 if(temp > max) max = temp;
1143 }
1144 }
1145 IDB.Nb_bits_pixel=2; // Find the minimum bpp value to fit all pixels
1146 while((int)max >= (1 << IDB.Nb_bits_pixel)) {
1147 IDB.Nb_bits_pixel++;
1148 }
1149 GFX2_Log(GFX2_DEBUG, "GIF image #%d %ubits (%u,%u) %ux%u\n",
1150 current_layer, IDB.Nb_bits_pixel, IDB.Pos_X, IDB.Pos_Y,
1151 IDB.Image_width, IDB.Image_height);
1152
1153 // On va écrire un block indicateur d'IDB et l'IDB du fichier
1154 block_identifier=0x2C;
1155 IDB.Indicator=0x07; // Image non entrelacée, pas de palette locale.
1156 clear = 1 << IDB.Nb_bits_pixel; // Clear Code
1157 eof = clear + 1; // End of Picture Code
1158
1159 if ( Write_byte(GIF_file,block_identifier) &&
1160 Write_word_le(GIF_file,IDB.Pos_X) &&
1161 Write_word_le(GIF_file,IDB.Pos_Y) &&
1162 Write_word_le(GIF_file,IDB.Image_width) &&
1163 Write_word_le(GIF_file,IDB.Image_height) &&
1164 Write_byte(GIF_file,IDB.Indicator) &&
1165 Write_byte(GIF_file,IDB.Nb_bits_pixel))
1166 {
1167 // Le block indicateur d'IDB et l'IDB ont étés correctements
1168 // écrits.
1169
1170 GIF.pos_X=IDB.Pos_X;
1171 GIF.pos_Y=IDB.Pos_Y;
1172 GIF.last_byte=0;
1173 GIF.remainder_bits=0;
1174 GIF.remainder_byte=0;
1175
1176 #define GIF_INVALID_CODE (65535)
1177 index=GIF_INVALID_CODE;
1178 File_error=0;
1179 GIF.stop=0;
1180
1181 // Réintialisation de la table:
1182 alphabet->free = clear + 2; // 258 for 8bpp
1183 GIF.nb_bits = IDB.Nb_bits_pixel + 1; // 9 for 8 bpp
1184 alphabet->max = clear+clear-1; // 511 for 8bpp
1185 GIF_set_code(GIF_file, &GIF, GIF_buffer, clear); //256 for 8bpp
1186 for (start=0; start<4096; start++)
1187 {
1188 alphabet->daughter[start] = GIF_INVALID_CODE;
1189 alphabet->sister[start] = GIF_INVALID_CODE;
1190 }
1191
1192 ////////////////////////////////////////////// COMPRESSION LZW //
1193
1194 start=current_string=GIF_next_pixel(context, &GIF, &IDB);
1195 descend=1;
1196
1197 while ((!GIF.stop) && (!File_error))
1198 {
1199 current_char=GIF_next_pixel(context, &GIF, &IDB);
1200
1201 // look for (current_string,current_char) in the alphabet
1202 while ( (index != GIF_INVALID_CODE) &&
1203 ( (current_string != alphabet->prefix[index]) ||
1204 (current_char != alphabet->suffix[index]) ) )
1205 {
1206 descend = 0;
1207 start = index;
1208 index = alphabet->sister[index];
1209 }
1210
1211 if (index != GIF_INVALID_CODE)
1212 {
1213 // (current_string,current_char) == (alphabet_prefix,alphabet_suffix)[index]
1214 // We have found (current_string,current_char) in the alphabet
1215 // at the index position. So go on and prepare for then next character
1216
1217 descend = 1;
1218 start = current_string = index;
1219 index = alphabet->daughter[index];
1220 }
1221 else
1222 {
1223 // (current_string,current_char) was not found in the alphabet
1224 // so write current_string to the Gif stream
1225 GIF_set_code(GIF_file, &GIF, GIF_buffer, current_string);
1226
1227 if(alphabet->free < 4096) {
1228 // link current_string and the new one
1229 if (descend)
1230 alphabet->daughter[start] = alphabet->free;
1231 else
1232 alphabet->sister[start] = alphabet->free;
1233
1234 // add (current_string,current_char) to the alphabet
1235 alphabet->prefix[alphabet->free] = current_string;
1236 alphabet->suffix[alphabet->free] = current_char;
1237 alphabet->free++;
1238 }
1239
1240 if (alphabet->free >= 4096)
1241 {
1242 // clear alphabet
1243 GIF_set_code(GIF_file, &GIF, GIF_buffer, clear); // 256 for 8bpp
1244 alphabet->free=clear+2; // 258 for 8bpp
1245 GIF.nb_bits = IDB.Nb_bits_pixel + 1; // 9 for 8bpp
1246 alphabet->max = clear+clear-1; // 511 for 8bpp
1247 for (start=0;start<4096;start++)
1248 {
1249 alphabet->daughter[start] = GIF_INVALID_CODE;
1250 alphabet->sister[start] = GIF_INVALID_CODE;
1251 }
1252 }
1253 else if (alphabet->free > (alphabet->max + 1))
1254 {
1255 // On augmente le nb de bits
1256
1257 GIF.nb_bits++;
1258 alphabet->max = (1<<GIF.nb_bits)-1;
1259 }
1260
1261 // initialize current_string as the string "current_char"
1262 index = alphabet->daughter[current_char];
1263 start = current_string = current_char;
1264 descend = 1;
1265 }
1266 }
1267
1268 if (!File_error)
1269 {
1270 // Write the last code (before EOF)
1271 GIF_set_code(GIF_file, &GIF, GIF_buffer, current_string);
1272
1273 // we need to update alphabet->free / GIF.nb_bits here because
1274 // the decoder will update them after each code,
1275 // so in very rare cases there might be a problem if we
1276 // don't do it.
1277 // see http://pulkomandy.tk/projects/GrafX2/ticket/125
1278 if(alphabet->free < 4096)
1279 {
1280 alphabet->free++;
1281 if ((alphabet->free > alphabet->max+1) && (GIF.nb_bits < 12))
1282 {
1283 GIF.nb_bits++;
1284 alphabet->max = (1 << GIF.nb_bits) - 1;
1285 }
1286 }
1287
1288 GIF_set_code(GIF_file, &GIF, GIF_buffer, eof); // 257 for 8bpp // Code de End d'image
1289 if (GIF.remainder_bits!=0)
1290 {
1291 // Write last byte (this is an incomplete byte)
1292 GIF_buffer[++GIF.remainder_byte]=GIF.last_byte;
1293 GIF.last_byte=0;
1294 GIF.remainder_bits=0;
1295 }
1296 GIF_empty_buffer(GIF_file, &GIF, GIF_buffer); // On envoie les dernières données du buffer GIF dans le buffer KM
1297
1298 // On écrit un \0
1299 if (! Write_byte(GIF_file,'\x00'))
1300 File_error=1;
1301 }
1302
1303 } // On a pu écrire l'IDB
1304 else
1305 File_error=1;
1306 }
1307 else
1308 File_error=1;
1309 }
1310
1311 // After writing all layers
1312 if (!File_error)
1313 {
1314 /// - If requested, write a specific extension for storing
1315 /// original file path.
1316 /// This is used by the backup system.
1317 /// The format is :
1318 /// <pre>
1319 /// 0x21 Extension Label
1320 /// 0xFF Application Extension Label
1321 /// 0x0B Block Size
1322 /// "GFX2PATH" "GFX2PATH" Application Identifier (8 bytes)
1323 /// "\0\0\0" Application Authentication Code (3 bytes)
1324 /// 0xll Sub-block Data Size : path size (including null)
1325 /// "..path.." path (null-terminated)
1326 /// 0xll Sub-block Data Size : filename size (including null)
1327 /// "..file.." file name (null-terminated)
1328 /// 0x00 Block terminator </pre>
1329 if (context->Original_file_name != NULL
1330 && context->Original_file_directory != NULL)
1331 {
1332 long name_size = 1+strlen(context->Original_file_name);
1333 long dir_size = 1+strlen(context->Original_file_directory);
1334 if (name_size<256 && dir_size<256)
1335 {
1336 if (! Write_bytes(GIF_file,"\x21\xFF\x0BGFX2PATH\x00\x00\x00", 14)
1337 || ! Write_byte(GIF_file,dir_size)
1338 || ! Write_bytes(GIF_file, context->Original_file_directory, dir_size)
1339 || ! Write_byte(GIF_file,name_size)
1340 || ! Write_bytes(GIF_file, context->Original_file_name, name_size)
1341 || ! Write_byte(GIF_file,0))
1342 File_error=1;
1343 }
1344 }
1345
1346 // On écrit un GIF TERMINATOR, exigé par SVGA et SEA.
1347 if (! Write_byte(GIF_file,'\x3B'))
1348 File_error=1;
1349 }
1350
1351 } // On a pu écrire la palette
1352 else
1353 File_error=1;
1354
1355 } // On a pu écrire le LSDB
1356 else
1357 File_error=1;
1358
1359 // Libération de la mémoire utilisée par les tables
1360 free(alphabet);
1361
1362 } // On a pu écrire la signature du fichier
1363 else
1364 File_error=1;
1365
1366 fclose(GIF_file);
1367 if (File_error)
1368 Remove_file(context);
1369
1370 } // On a pu ouvrir le fichier en écriture
1371 else
1372 File_error=1;
1373
1374 }
1375
1376 /** @} */
1377