Chan-Vese Segmentation
gifwrite.c
Go to the documentation of this file.
1 
20 #include <stdio.h>
21 #include <stdlib.h>
22 
24 #define MAXBITS 12
25 
26 #define MAXCODE 4095
27 
28 #define TABLESIZE 5003
29 
30 #define HASHSHIFT 4
31 
32 #define UNUSED -1
33 
34 
36 typedef struct
37 {
38  long Hash;
39  unsigned short Code;
40 } tableentry;
41 
43 typedef struct
44 {
45  FILE *File;
46  unsigned int BitsPerCode;
47  unsigned int BitAccum;
48  int NumBits;
49  int BlockSize;
50  unsigned char Block[255];
51 } bitstream;
52 
53 static void WriteWordLE(FILE *File, unsigned short Value);
54 static void WriteImageData(FILE *File, tableentry *Table,
55  unsigned char *Data, int FrameLeft, int FrameTop,
56  int FrameWidth, int FrameHeight, int ImageWidth);
57 static void CropFrame(int *FrameLeft, int *FrameTop,
58  int *FrameWidth, int *FrameHeight, unsigned char *Data,
59  int ImageWidth, int ImageHeight, int TransparentColor);
60 
61 
120 int GifWrite(unsigned char **Image,
121  int ImageWidth, int ImageHeight, int NumFrames,
122  const unsigned char *Palette, int NumColors, int TransparentColor,
123  const int *Delays, const char *OutputFile)
124 {
125  FILE *File = NULL;
126  tableentry *Table = NULL;
127  const int NumPixels = ImageWidth*ImageHeight;
128  int i, Frame, TableSizePow;
129  int FrameLeft, FrameTop, FrameWidth, FrameHeight, Success = 0;
130 
131  /* Input checking */
132  if(!Image || !Palette || !OutputFile || ImageWidth <= 0
133  || ImageHeight <= 0 || NumFrames <= 0 || NumColors <= 2
134  || TransparentColor < 0 || TransparentColor >= NumColors)
135  return 0;
136 
137  for(Frame = 0; Frame < NumFrames; Frame++)
138  {
139  if(!Image[Frame])
140  return 0;
141 
142  for(i = 0; i < NumPixels; i++)
143  if(Image[Frame][i] >= NumColors)
144  {
145  fprintf(stderr, "Pixel values exceed palette.\n");
146  return 0;
147  }
148  }
149 
150  if(!(Table = (tableentry *)malloc(sizeof(tableentry)*TABLESIZE)))
151  return 0;
152 
153  if(!(File = fopen(OutputFile, "wb")))
154  {
155  fprintf(stderr, "Unable to open \"%s\" for writing.\n", OutputFile);
156  goto Catch;
157  }
158 
159  for(TableSizePow = 1; TableSizePow < NumColors && TableSizePow < 8;)
160  TableSizePow++;
161 
162  /* GIF Header */
163  fwrite("GIF89a", 1, 6, File);
164  WriteWordLE(File, (unsigned short)ImageWidth);
165  WriteWordLE(File, (unsigned short)ImageHeight);
166  putc(0xF0 | (TableSizePow - 1), File);
167  WriteWordLE(File, 0x0000);
168  fwrite(Palette, 1, 3*NumColors, File);
169 
170  /* Pad unused palette entries with 0 */
171  for(i = 3*((1 << TableSizePow) - NumColors); i > 0; i--)
172  putc(0x00, File);
173 
174  /* Netscape animation extension */
175  if(NumFrames > 1)
176  fwrite("\x21\xFF\x0BNETSCAPE2.0\x03\x01\xFF\xFF", 1, 19, File);
177 
178  for(Frame = 0; Frame < NumFrames; Frame++)
179  {
180  CropFrame(&FrameLeft, &FrameTop, &FrameWidth, &FrameHeight,
181  Image[Frame], ImageWidth, ImageHeight, TransparentColor);
182 
183  /* Write Graphic control extension and frame descriptor */
184  WriteWordLE(File, 0xF921); /* Graphic control label */
185  WriteWordLE(File, 0x0504); /* Size and packed fields */
186  WriteWordLE(File, (Delays) ? Delays[Frame] : 10);
187  putc(TransparentColor, File);
188  WriteWordLE(File, 0x2C00); /* Begin frame descriptor */
189  WriteWordLE(File, (unsigned short)FrameLeft);
190  WriteWordLE(File, (unsigned short)FrameTop);
191  WriteWordLE(File, (unsigned short)FrameWidth);
192  WriteWordLE(File, (unsigned short)FrameHeight);
193  putc(0x00, File); /* No local color table */
194 
195  /* Write the current frame */
196  WriteImageData(File, Table, Image[Frame], FrameLeft, FrameTop,
197  FrameWidth, FrameHeight, ImageWidth);
198  }
199 
200  putc(0x3B, File); /* File terminator */
201 
202  if(ferror(File))
203  {
204  fprintf(stderr, "Error while writing to \"%s\".\n", OutputFile);
205  goto Catch;
206  }
207 
208  Success = 1;
209 Catch:
210  if(File)
211  fclose(File);
212  if(Table)
213  free(Table);
214  return Success;
215 }
216 
217 
219 static void WriteWordLE(FILE *File, unsigned short Value)
220 {
221  putc(Value & 0xFF, File);
222  putc((Value & 0xFF00) >> 8, File);
223 }
224 
225 
227 static void FlushBlock(bitstream *Stream)
228 {
229  putc(Stream->BlockSize, Stream->File); /* Write the size of the block */
230  fwrite(Stream->Block, 1, Stream->BlockSize, Stream->File);
231  Stream->BlockSize = 0;
232 }
233 
234 
236 static void FlushBits(bitstream *Stream, int MaxRemaining)
237 {
238  for(; Stream->NumBits > MaxRemaining;
239  Stream->BitAccum >>= 8, Stream->NumBits -= 8)
240  {
241  Stream->Block[Stream->BlockSize] =
242  (unsigned char)(Stream->BitAccum & 0xFF);
243 
244  if((++Stream->BlockSize) == 255)
245  FlushBlock(Stream);
246  }
247 }
248 
249 
251 static void WriteBits(bitstream *Stream, unsigned short Code)
252 {
253  Stream->BitAccum |= (Stream->NumBits == 0) ?
254  Code : (Code << Stream->NumBits);
255  Stream->NumBits += Stream->BitsPerCode;
256  FlushBits(Stream, 7);
257 }
258 
259 
261 static void WriteImageData(FILE *File, tableentry *Table,
262  unsigned char *Data, int FrameLeft, int FrameTop,
263  int FrameWidth, int FrameHeight, int ImageWidth)
264 {
265  bitstream Stream;
266  long Hash;
267  unsigned short ClearCode, FreeCode, Prefix, NextRaise;
268  unsigned int AppendChar, InitBitsPerCode = 9;
269  int x, y, i, Step;
270 
271  Stream.File = File;
272  Stream.BitsPerCode = InitBitsPerCode;
273  Stream.BitAccum = Stream.NumBits = Stream.BlockSize = 0;
274  ClearCode = (unsigned short)(1 << (InitBitsPerCode - 1));
275  NextRaise = (unsigned short)(1 << InitBitsPerCode);
276  FreeCode = ClearCode + 2;
277 
278  for(i = 0; i < TABLESIZE; i++)
279  Table[i].Hash = UNUSED;
280 
281  putc(InitBitsPerCode - 1, File);
282 
283  /* Get the first character (top left corner of the frame) */
284  Prefix = Data[FrameLeft + ImageWidth*FrameTop];
285 
286  /* Loop over the pixels of the frame in row-major order. The following is
287  equivalent to the nested loop
288  for(y = 0; y < FrameHeight; y++)
289  for(x = 0; x < FrameWidth; x++)
290  but starting from x=1, y=0 instead of x=0, y=0.*/
291  for(x = 1, y = 0; y < FrameHeight; (++x >= FrameWidth) ? (x = 0, y++) : 0)
292  {
293  /* Get the next character */
294  AppendChar = Data[(x + FrameLeft) + ImageWidth*(y + FrameTop)];
295 
296  /* Search for Prefix+AppendChar in the Table */
297  Hash = (long)Prefix + (AppendChar << MAXBITS);
298  i = (AppendChar << HASHSHIFT) ^ Prefix;
299  Step = (i == 0) ? 1 : (TABLESIZE - i);
300 
301  while(Table[i].Hash != Hash && Table[i].Hash != UNUSED)
302  if((i -= Step) < 0)
303  i += TABLESIZE;
304 
305  if(Table[i].Hash != UNUSED)
306  {
307  /* Set Prefix <- Prefix+AppendChar */
308  Prefix = Table[i].Code;
309  continue;
310  }
311 
312  /* If we get here, Prefix+AppendChar is not in the Table. */
313  WriteBits(&Stream, Prefix);
314 
315  if(FreeCode < MAXCODE)
316  {
317  /* Increase BitsPerCode if necessary */
318  if(FreeCode == NextRaise)
319  {
320  Stream.BitsPerCode++;
321  NextRaise *= 2;
322  }
323 
324  /* Add Prefix+AppendChar to the Table. */
325  Table[i].Hash = Hash;
326  Table[i].Code = FreeCode++;
327  }
328  else
329  { /* There are no free codes left, clear the Table. */
330  WriteBits(&Stream, ClearCode);
331  Stream.BitsPerCode = InitBitsPerCode;
332  NextRaise = (unsigned short)(1 << InitBitsPerCode);
333  FreeCode = ClearCode + 2;
334 
335  for(i = 0; i < TABLESIZE; i++)
336  Table[i].Hash = UNUSED;
337  }
338 
339  Prefix = AppendChar;
340  }
341 
342  /* Flush buffers and write ending codes */
343  WriteBits(&Stream, Prefix);
344  WriteBits(&Stream, ClearCode + 1);
345  FlushBits(&Stream, 0);
346 
347  if(Stream.BlockSize > 0)
348  FlushBlock(&Stream);
349 
350  putc(0, File);
351 }
352 
353 
355 static void CropFrame(int *FrameLeft, int *FrameTop,
356  int *FrameWidth, int *FrameHeight, unsigned char *Data,
357  int ImageWidth, int ImageHeight, int TransparentColor)
358 {
359  int x, y, Left = ImageWidth, Right = 0, Top = ImageHeight, Bottom = 0;
360 
361  for(y = 0; y < ImageHeight; y++, Data += ImageWidth)
362  for(x = 0; x < ImageWidth; x++)
363  if(Data[x] != TransparentColor)
364  {
365  if(x < Left)
366  Left = x;
367  if(x > Right)
368  Right = x;
369  if(y < Top)
370  Top = y;
371  if(y > Bottom)
372  Bottom = y;
373  }
374 
375  if(Left == ImageWidth)
376  {
377  *FrameLeft = *FrameTop = 0;
378  *FrameWidth = *FrameHeight = 1;
379  }
380  else
381  {
382  *FrameLeft = Left;
383  *FrameTop = Top;
384  *FrameWidth = Right - Left + 1;
385  *FrameHeight = Bottom - Top + 1;
386  }
387 }
388 
389 
401 void FrameDifference(unsigned char **Image,
402  int ImageWidth, int ImageHeight, int NumFrames, int TransparentColor)
403 {
404  const int NumPixels = ImageWidth*ImageHeight;
405  int i, Frame, PrevFrame;
406 
407  /* Input checking */
408  if(!Image || ImageWidth <= 0 || ImageHeight <= 0 || NumFrames <= 0)
409  return;
410 
411  for(Frame = 0; Frame < NumFrames; Frame++)
412  if(!Image[Frame])
413  return;
414 
415  for(Frame = NumFrames - 1; Frame > 0; Frame--)
416  for(i = 0; i < NumPixels; i++)
417  {
418  if(Image[Frame][i] == TransparentColor)
419  continue;
420 
421  /* Find most recent frame where ith pixel is nontransparent */
422  for(PrevFrame = Frame - 1; PrevFrame >= 0; PrevFrame--)
423  if(Image[PrevFrame][i] != TransparentColor)
424  break;
425 
426  /* Set pixel to transparent if previous pixel is the same color */
427  if(PrevFrame >= 0 && Image[PrevFrame][i] == Image[Frame][i])
428  Image[Frame][i] = TransparentColor;
429  }
430 }