Optimize GIF size by using only the number of bits needed in LZW codes

It was always storing images in 8bpp whatever the values of pixels are,
so the minimum LZW code size was 9 bits.

Now the bpp is chosen according to the maximum pixel value.

Please note that the Whole 256 color palette is still stored entirely,
so this commit doesn't change the user experience in anyway, except saving
a few bytes :)
This commit is contained in:
Thomas Bernard 2017-12-19 10:00:08 +01:00
parent 51bb3057a3
commit 834050c2c4

View File

@ -2681,6 +2681,9 @@ void Save_GIF(T_IO_Context * context)
word index; // index de recherche de chaîne word index; // index de recherche de chaîne
int current_layer; int current_layer;
word clear; // LZW clear code
word eof; // End of image code
/////////////////////////////////////////////////// FIN DES DECLARATIONS // /////////////////////////////////////////////////// FIN DES DECLARATIONS //
File_error=0; File_error=0;
@ -2714,6 +2717,14 @@ void Save_GIF(T_IO_Context * context)
LSDB.Height=context->Height; LSDB.Height=context->Height;
} }
LSDB.Resol =0x97; // Image en 256 couleurs, avec une palette LSDB.Resol =0x97; // Image en 256 couleurs, avec une palette
// 0x97 = 1001 0111
// <Packed Fields> = Global Color Table Flag 1 Bit
// Color Resolution 3 Bits
// Sort Flag 1 Bit
// Size of Global Color Table 3 Bits
// TODO XXX Color resolution should be set to 7 = 8bit per RGB component
// it is set to 1 => 2bit per RGB component...
// I guess most decoders are ignoring it anyway
LSDB.Backcol=context->Transparent_color; LSDB.Backcol=context->Transparent_color;
switch(context->Ratio) switch(context->Ratio)
{ {
@ -2834,6 +2845,19 @@ void Save_GIF(T_IO_Context * context)
&& Write_byte(GIF_file,GCE.Block_terminator) && Write_byte(GIF_file,GCE.Block_terminator)
) )
{ {
// first look for the maximum pixel value
// to decide how many bit per pixel are needed.
byte temp, max = 0;
for(GIF_pos_Y = 0; GIF_pos_Y < context->Height; GIF_pos_Y++) {
for(GIF_pos_X = 0; GIF_pos_X < context->Width; GIF_pos_X++) {
temp=Get_pixel(context, GIF_pos_X, GIF_pos_Y);
if(temp > max) max = temp;
}
}
IDB.Nb_bits_pixel=2; // Find the minimum bpp value to fit all pixels
while((int)max >= (1 << IDB.Nb_bits_pixel)) {
IDB.Nb_bits_pixel++;
}
// On va écrire un block indicateur d'IDB et l'IDB du fichier // On va écrire un block indicateur d'IDB et l'IDB du fichier
block_identifier=0x2C; block_identifier=0x2C;
@ -2842,7 +2866,8 @@ void Save_GIF(T_IO_Context * context)
IDB.Image_width=context->Width; IDB.Image_width=context->Width;
IDB.Image_height=context->Height; IDB.Image_height=context->Height;
IDB.Indicator=0x07; // Image non entrelacée, pas de palette locale. IDB.Indicator=0x07; // Image non entrelacée, pas de palette locale.
IDB.Nb_bits_pixel=8; // Image 256 couleurs; clear = 1 << IDB.Nb_bits_pixel; // Clear Code
eof = clear + 1; // End of Picture Code
if ( Write_byte(GIF_file,block_identifier) && if ( Write_byte(GIF_file,block_identifier) &&
Write_word_le(GIF_file,IDB.Pos_X) && Write_word_le(GIF_file,IDB.Pos_X) &&
@ -2866,10 +2891,10 @@ void Save_GIF(T_IO_Context * context)
GIF_stop=0; GIF_stop=0;
// Réintialisation de la table: // Réintialisation de la table:
alphabet_free=258; alphabet_free=clear + 2; // 258 for 8bpp
GIF_nb_bits =9; GIF_nb_bits =IDB.Nb_bits_pixel + 1; // 9 for 8 bpp
alphabet_max =511; alphabet_max =clear+clear-1; // 511 for 8bpp
GIF_set_code(256); GIF_set_code(clear); //256 for 8bpp
for (start=0;start<4096;start++) for (start=0;start<4096;start++)
{ {
alphabet_daughter[start]=4096; alphabet_daughter[start]=4096;
@ -2924,10 +2949,10 @@ void Save_GIF(T_IO_Context * context)
if (alphabet_free>0xFFF) if (alphabet_free>0xFFF)
{ {
// Réintialisation de la table: // Réintialisation de la table:
GIF_set_code(256); GIF_set_code(clear); // 256 for 8bpp
alphabet_free=258; alphabet_free=clear+2; // 258 for 8bpp
GIF_nb_bits =9; GIF_nb_bits =IDB.Nb_bits_pixel + 1; // 9 for 8bpp
alphabet_max =511; alphabet_max =clear+clear-1; // 511 for 8bpp
for (start=0;start<4096;start++) for (start=0;start<4096;start++)
{ {
alphabet_daughter[start]=4096; alphabet_daughter[start]=4096;
@ -2990,7 +3015,7 @@ void Save_GIF(T_IO_Context * context)
} }
*/ */
GIF_set_code(257); // Code de End d'image GIF_set_code(eof); // 257 for 8bpp // Code de End d'image
if (GIF_remainder_bits!=0) if (GIF_remainder_bits!=0)
GIF_set_code(0); // Code bidon permettant de s'assurer que tous les bits du dernier code aient bien étés inscris dans le buffer GIF GIF_set_code(0); // Code bidon permettant de s'assurer que tous les bits du dernier code aient bien étés inscris dans le buffer GIF
GIF_empty_buffer(); // On envoie les dernières données du buffer GIF dans le buffer KM GIF_empty_buffer(); // On envoie les dernières données du buffer GIF dans le buffer KM