Subversion Repositories wimsdev

Rev

Rev 10 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
10 reyssat 1
/*
2
 * gifencode.c
3
 *
4
 * Copyright (c) 1997,1998 by Hans Dinsen-Hansen
5
 * The algorithms are inspired by those of gifcode.c
6
 * Copyright (c) 1995,1996 Michael A. Mayer
7
 * All rights reserved.
8
 *
9
 * This software may be freely copied, modified and redistributed
10
 * without fee provided that above copyright notices are preserved
11
 * intact on all copies and modified copies.
12
 *
13
 * There is no warranty or other guarantee of fitness of this software.
14
 * It is provided solely "as is". The author(s) disclaim(s) all
15
 * responsibility and liability with respect to this software's usage
16
 * or its effect upon hardware or computer systems.
17
 *
18
 * The Graphics Interchange format (c) is the Copyright property of
19
 * Compuserve Incorporated.  Gif(sm) is a Service Mark property of
20
 * Compuserve Incorporated.
21
 *
22
 *
23
 *           Implements GIF encoding by means of a tree search.
24
 *           --------------------------------------------------
25
 *
26
 *  - The string table may be thought of being stored in a "b-tree of
27
 * steroids," or more specifically, a {256,128,...,4}-tree, depending on
28
 * the size of the color map.
29
 *  - Each (non-NULL) node contains the string table index (or code) and
30
 * {256,128,...,4} pointers to other nodes.
31
 *  - For example, the index associated with the string 0-3-173-25 would be
32
 * stored in:
33
 *       first->node[0]->node[3]->node[173]->node[25]->code
34
 *
35
 *  - Speed and effectivity considerations, however, have made this
36
 * implementation somewhat obscure, because it is costly to initialize
37
 * a node-array where most elements will never be used.
38
 *  - Initially, a new node will be marked as terminating, TERMIN.
39
 * If this node is used at a later stage, its mark will be changed.
40
 *  - Only nodes with several used nodes will be associated with a
41
 * node-array.  Such nodes are marked LOOKUP.
42
 *  - The remaining nodes are marked SEARCH.  They are linked together
43
 * in a search-list, where a field, NODE->alt, points at an alternative
44
 * following color.
45
 *  - It is hardly feasible exactly to predict which nodes will have most
46
 * used node pointers.  The theory here is that the very first node as
47
 * well as the first couple of nodes which need at least one alternative
48
 * color, will be among the ones with many nodes ("... whatever that
49
 * means", as my tutor in Num. Analysis and programming used to say).
12195 bpr 50
 *  - The number of possible LOOKUP nodes depends on the size of the color
10 reyssat 51
 * map.  Large color maps will have many SEARCH nodes; small color maps
52
 * will probably have many LOOKUP nodes.
53
*/
54
 
55
#include "whirlgif.h"
56
 
57
#define BLOKLEN 255
58
#define BUFLEN 1000
59
 
60
 
61
int chainlen = 0, maxchainlen = 0, nodecount = 0, lookuptypes = 0, nbits;
62
short need = 8;
63
GifTree *empty[256], GifRoot = {LOOKUP, 0, 0, empty, NULL, NULL},
64
        *topNode, *baseNode, **nodeArray, **lastArray;
65
 
66
extern unsigned int debugFlag, verbose;
67
extern int count;
68
 
69
void GifEncode(FILE *fout, UBYTE *pixels, int depth, int siz)
70
{
71
  GifTree *first = &GifRoot, *newNode, *curNode;
72
  UBYTE   *end;
73
  int     cc, eoi, next, tel=0;
74
  short   cLength;
75
 
76
  char    *pos, *buffer;
77
 
78
  empty[0] = NULL;
79
  need = 8;
80
 
81
  nodeArray = empty;
82
  memmove(++nodeArray, empty, 255*sizeof(GifTree **));
83
  if (( buffer = (char *)malloc((BUFLEN+1)*sizeof(char))) == NULL )
84
         TheEnd1("No memory for writing");
85
  buffer++;
86
 
87
 
88
  pos = buffer;
89
  buffer[0] = 0x0;
90
 
91
  cc = (depth == 1) ? 0x4 : 1<<depth;
92
  fputc((depth == 1) ? 2 : depth, fout);
93
  eoi = cc+1;
94
  next = cc+2;
95
 
96
  cLength = (depth == 1) ? 3 : depth+1;
97
 
98
  if (( topNode = baseNode = (GifTree *)malloc(sizeof(GifTree)*4094)) == NULL )
99
         TheEnd1("No memory for GIF-code tree");
100
  if (( nodeArray = first->node = (GifTree **)malloc(256*sizeof(GifTree *)*noOfArrays)) == NULL )
101
         TheEnd1("No memory for search nodes");
102
  lastArray = nodeArray + ( 256*noOfArrays - cc);
103
  ClearTree(cc, first);
104
 
105
  pos = AddCodeToBuffer(cc, cLength, pos);
106
 
107
  end = pixels+siz;
108
  curNode = first;
109
  while(pixels < end) {
110
 
111
    if ( curNode->node[*pixels] != NULL ) {
112
      curNode = curNode->node[*pixels];
113
      tel++;
114
      pixels++;
115
      chainlen++;
116
      continue;
117
    } else if ( curNode->typ == SEARCH ) {
118
      newNode = curNode->nxt;
119
      while ( newNode->alt != NULL ) {
120
        if ( newNode->ix == *pixels ) break;
121
        newNode = newNode->alt;
122
      }
123
      if (newNode->ix == *pixels ) {
124
        tel++;
125
        pixels++;
126
        chainlen++;
127
        curNode = newNode;
128
        continue;
129
      }
130
    }
131
 
132
/* ******************************************************
133
 * If there is no more thread to follow, we create a new node.  If the
134
 * current node is terminating, it will become a SEARCH node.  If it is
135
 * a SEARCH node, and if we still have room, it will be converted to a
136
 * LOOKUP node.
137
*/
138
  newNode = ++topNode;
139
  switch (curNode->typ ) {
140
   case LOOKUP:
141
     newNode->nxt = NULL;
142
     newNode->alt = NULL,
143
     curNode->node[*pixels] = newNode;
144
   break;
145
   case SEARCH:
146
     if ( nodeArray != lastArray ) {
147
       nodeArray += cc;
148
       curNode->node = nodeArray;
149
       curNode->typ = LOOKUP;
150
       curNode->node[*pixels] = newNode;
151
       curNode->node[(curNode->nxt)->ix] = curNode->nxt;
152
       lookuptypes++;
153
       newNode->nxt = NULL;
154
       newNode->alt = NULL,
155
       curNode->nxt = NULL;
156
       break;
157
     }
158
/*   otherwise do as we do with a TERMIN node  */
159
   case TERMIN:
160
     newNode->alt = curNode->nxt;
161
     newNode->nxt = NULL,
162
     curNode->nxt = newNode;
163
     curNode->typ = SEARCH;
164
     break;
165
   default:
166
     fprintf(stderr, "Silly node type: %d\n", curNode->typ);
167
  }
168
  newNode->code = next;
169
  newNode->ix = *pixels;
170
  newNode->typ = TERMIN;
171
  newNode->node = empty;
172
  nodecount++;
173
/*
174
* End of node creation
175
* ******************************************************
176
*/
177
  if (debugFlag) {
178
    if (curNode == newNode) fprintf(stderr, "Wrong choice of node\n");
179
    if ( curNode->typ == LOOKUP && curNode->node[*pixels] != newNode ) fprintf(stderr, "Wrong pixel coding\n");
180
    if ( curNode->typ == TERMIN ) fprintf(stderr, "Wrong Type coding; frame no = %d; pixel# = %d; nodecount = %d\n", count, tel, nodecount);
181
  }
182
    pos = AddCodeToBuffer(curNode->code, cLength, pos);
183
    if ( chainlen > maxchainlen ) maxchainlen = chainlen;
184
    chainlen = 0;
185
    if(pos-buffer>BLOKLEN) {
12195 bpr 186
      buffer[-1] = (char)BLOKLEN;
10 reyssat 187
      fwrite(buffer-1, 1, BLOKLEN+1, fout);
188
      buffer[0] = buffer[BLOKLEN];
189
      buffer[1] = buffer[BLOKLEN+1];
190
      buffer[2] = buffer[BLOKLEN+2];
191
      buffer[3] = buffer[BLOKLEN+3];
192
      pos -= BLOKLEN;
193
    }
194
    curNode = first;
195
 
196
    if(next == (1<<cLength)) cLength++;
197
    next++;
198
 
199
    if(next == 0xfff) {
200
      ClearTree(cc,first);
201
      pos = AddCodeToBuffer(cc, cLength, pos);
202
      if(pos-buffer>BLOKLEN) {
12195 bpr 203
        buffer[-1] = (char)BLOKLEN;
10 reyssat 204
        fwrite(buffer-1, 1, BLOKLEN+1, fout);
205
        buffer[0] = buffer[BLOKLEN];
206
        buffer[1] = buffer[BLOKLEN+1];
207
        buffer[2] = buffer[BLOKLEN+2];
208
        buffer[3] = buffer[BLOKLEN+3];
209
        pos -= BLOKLEN;
210
      }
211
      next = cc+2;
212
      cLength = (depth == 1)?3:depth+1;
213
    }
214
  }
215
 
216
  pos = AddCodeToBuffer(curNode->code, cLength, pos);
217
  if(pos-buffer>BLOKLEN-3) {
12195 bpr 218
    buffer[-1] = (char)BLOKLEN-3;
10 reyssat 219
    fwrite(buffer-1, 1, BLOKLEN-2, fout);
220
    buffer[0] = buffer[BLOKLEN-3];
221
    buffer[1] = buffer[BLOKLEN-2];
222
    buffer[2] = buffer[BLOKLEN-1];
223
    buffer[3] = buffer[BLOKLEN];
224
    buffer[4] = buffer[BLOKLEN+1];
225
    pos -= BLOKLEN-3;
226
  }
227
  pos = AddCodeToBuffer(eoi, cLength, pos);
228
  pos = AddCodeToBuffer(0x0, -1, pos);
229
  buffer[-1] = pos-buffer;
230
 
231
  fwrite(buffer-1, pos-buffer+1, 1, fout);
232
  free(buffer-1); free(first->node); free(baseNode);
233
  if (debugFlag) fprintf(stderr, "pixel count = %d; nodeCount = %d lookup nodes = %d\n", tel, nodecount, lookuptypes);
234
  return;
235
 
236
}
237
 
238
void ClearTree(int cc, GifTree *root)
239
{
240
  int i;
241
  GifTree *newNode, **xx;
242
 
243
  if (debugFlag>1) fprintf(stderr, "Clear Tree  cc= %d\n", cc);
244
  if (debugFlag>1) fprintf(stderr, "nodeCount = %d lookup nodes = %d\n", nodecount, lookuptypes);
245
  maxchainlen=0; lookuptypes = 1;
246
  nodecount = 0;
247
  nodeArray = root->node;
248
  xx= nodeArray;
249
  for (i = 0; i < noOfArrays; i++ ) {
250
    memmove (xx, empty, 256*sizeof(GifTree **));
251
    xx += 256;
252
  }
253
  topNode = baseNode;
254
  for(i=0; i<cc; i++) {
255
    root->node[i] = newNode = ++topNode;
256
    newNode->nxt = NULL;
257
    newNode->alt = NULL;
258
    newNode->code = i;
259
    newNode->ix = i;
260
    newNode->typ = TERMIN;
261
    newNode->node = empty;
262
    nodecount++;
263
  }
264
}
265
 
266
char *AddCodeToBuffer(int code, short n, char *buf)
267
{
268
  int    mask;
269
 
270
  if(n<0) {
271
    if(need<8) {
272
      buf++;
273
      *buf = 0x0;
274
    }
275
    need = 8;
276
    return buf;
277
  }
278
 
279
  while(n>=need) {
280
    mask = (1<<need)-1;
281
    *buf += (mask&code)<<(8-need);
282
    buf++;
283
    *buf = 0x0;
284
    code = code>>need;
285
    n -= need;
286
    need = 8;
287
  }
288
  if(n) {
289
    mask = (1<<n)-1;
290
    *buf += (mask&code)<<(8-need);
291
    need -= n;
292
  }
293
  return buf;
294
}