blob: a72a9741251e1897faf63414a3bd42e371415497 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% GGGG IIIII FFFFF %
7% G I F %
8% G GG I FFF %
9% G G I F %
10% GGG IIIII F %
11% %
12% %
13% Read/Write Compuserv Graphics Interchange Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/color-private.h"
cristye7e40552010-04-24 21:34:22 +000048#include "magick/colormap.h"
49#include "magick/colormap-private.h"
cristy3ed852e2009-09-05 21:47:34 +000050#include "magick/colorspace.h"
51#include "magick/exception.h"
52#include "magick/exception-private.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/profile.h"
57#include "magick/magick.h"
58#include "magick/memory_.h"
59#include "magick/monitor.h"
60#include "magick/monitor-private.h"
61#include "magick/option.h"
62#include "magick/property.h"
63#include "magick/quantize.h"
64#include "magick/quantum-private.h"
65#include "magick/static.h"
66#include "magick/string_.h"
67#include "magick/module.h"
68
69/*
70 Define declarations.
71*/
72#define MaximumLZWBits 12
73#define MaximumLZWCode (1UL << MaximumLZWBits)
74
75/*
76 Typdef declarations.
77*/
78typedef struct _LZWCodeInfo
79{
80 unsigned char
81 buffer[280];
82
cristybb503372010-05-27 20:51:26 +000083 size_t
cristy3ed852e2009-09-05 21:47:34 +000084 count,
85 bit;
86
87 MagickBooleanType
88 eof;
89} LZWCodeInfo;
90
91typedef struct _LZWStack
92{
cristybb503372010-05-27 20:51:26 +000093 size_t
cristy3ed852e2009-09-05 21:47:34 +000094 *codes,
95 *index,
96 *top;
97} LZWStack;
98
99typedef struct _LZWInfo
100{
101 Image
102 *image;
103
104 LZWStack
105 *stack;
106
107 MagickBooleanType
108 genesis;
109
cristybb503372010-05-27 20:51:26 +0000110 size_t
cristy3ed852e2009-09-05 21:47:34 +0000111 data_size,
112 maximum_data_value,
113 clear_code,
114 end_code,
115 bits,
116 first_code,
117 last_code,
118 maximum_code,
119 slot,
120 *table[2];
121
122 LZWCodeInfo
123 code_info;
124} LZWInfo;
125
126/*
127 Forward declarations.
128*/
129static inline int
cristybb503372010-05-27 20:51:26 +0000130 GetNextLZWCode(LZWInfo *,const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000131
132static MagickBooleanType
133 WriteGIFImage(const ImageInfo *,Image *);
134
135static ssize_t
136 ReadBlobBlock(Image *,unsigned char *);
137
138/*
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140% %
141% %
142% %
143% D e c o d e I m a g e %
144% %
145% %
146% %
147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148%
149% DecodeImage uncompresses an image via GIF-coding.
150%
151% The format of the DecodeImage method is:
152%
cristybb503372010-05-27 20:51:26 +0000153% MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
cristy3ed852e2009-09-05 21:47:34 +0000154%
155% A description of each parameter follows:
156%
157% o image: the address of a structure of type Image.
158%
159% o opacity: The colormap index associated with the transparent color.
160%
161*/
162
163static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
164{
cristybb503372010-05-27 20:51:26 +0000165 if (lzw_info->table[0] != (size_t *) NULL)
166 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000167 lzw_info->table[0]);
cristybb503372010-05-27 20:51:26 +0000168 if (lzw_info->table[1] != (size_t *) NULL)
169 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000170 lzw_info->table[1]);
171 if (lzw_info->stack != (LZWStack *) NULL)
172 {
cristybb503372010-05-27 20:51:26 +0000173 if (lzw_info->stack->codes != (size_t *) NULL)
174 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000175 lzw_info->stack->codes);
176 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
177 }
178 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
179 return((LZWInfo *) NULL);
180}
181
182static inline void ResetLZWInfo(LZWInfo *lzw_info)
183{
cristyeaedf062010-05-29 22:36:02 +0000184 size_t
185 one;
186
cristy3ed852e2009-09-05 21:47:34 +0000187 lzw_info->bits=lzw_info->data_size+1;
cristy5d89f432010-05-30 00:00:26 +0000188 one=1;
cristyeaedf062010-05-29 22:36:02 +0000189 lzw_info->maximum_code=one << lzw_info->bits;
cristy3ed852e2009-09-05 21:47:34 +0000190 lzw_info->slot=lzw_info->maximum_data_value+3;
191 lzw_info->genesis=MagickTrue;
192}
193
cristybb503372010-05-27 20:51:26 +0000194static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000195{
196 LZWInfo
197 *lzw_info;
198
cristybb503372010-05-27 20:51:26 +0000199 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000200 i;
201
cristy5d89f432010-05-30 00:00:26 +0000202 size_t
203 one;
204
cristy73bd4a52010-10-05 11:24:23 +0000205 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
cristy3ed852e2009-09-05 21:47:34 +0000206 if (lzw_info == (LZWInfo *) NULL)
207 return((LZWInfo *) NULL);
208 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
209 lzw_info->image=image;
210 lzw_info->data_size=data_size;
cristy5d89f432010-05-30 00:00:26 +0000211 one=1;
212 lzw_info->maximum_data_value=(one << data_size)-1;
cristy3ed852e2009-09-05 21:47:34 +0000213 lzw_info->clear_code=lzw_info->maximum_data_value+1;
214 lzw_info->end_code=lzw_info->maximum_data_value+2;
cristybb503372010-05-27 20:51:26 +0000215 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
cristy3ed852e2009-09-05 21:47:34 +0000216 sizeof(*lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000217 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
cristy3ed852e2009-09-05 21:47:34 +0000218 sizeof(*lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000219 if ((lzw_info->table[0] == (size_t *) NULL) ||
220 (lzw_info->table[1] == (size_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000221 {
222 lzw_info=RelinquishLZWInfo(lzw_info);
223 return((LZWInfo *) NULL);
224 }
cristybb503372010-05-27 20:51:26 +0000225 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
cristy3ed852e2009-09-05 21:47:34 +0000226 {
227 lzw_info->table[0][i]=0;
cristybb503372010-05-27 20:51:26 +0000228 lzw_info->table[1][i]=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +0000229 }
230 ResetLZWInfo(lzw_info);
231 lzw_info->code_info.buffer[0]='\0';
232 lzw_info->code_info.buffer[1]='\0';
233 lzw_info->code_info.count=2;
234 lzw_info->code_info.bit=8*lzw_info->code_info.count;
235 lzw_info->code_info.eof=MagickFalse;
236 lzw_info->genesis=MagickTrue;
cristy73bd4a52010-10-05 11:24:23 +0000237 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
cristy3ed852e2009-09-05 21:47:34 +0000238 if (lzw_info->stack == (LZWStack *) NULL)
239 {
240 lzw_info=RelinquishLZWInfo(lzw_info);
241 return((LZWInfo *) NULL);
242 }
cristybb503372010-05-27 20:51:26 +0000243 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
cristy3ed852e2009-09-05 21:47:34 +0000244 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
cristybb503372010-05-27 20:51:26 +0000245 if (lzw_info->stack->codes == (size_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000246 {
247 lzw_info=RelinquishLZWInfo(lzw_info);
248 return((LZWInfo *) NULL);
249 }
250 lzw_info->stack->index=lzw_info->stack->codes;
251 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
252 return(lzw_info);
253}
254
cristybb503372010-05-27 20:51:26 +0000255static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
cristy3ed852e2009-09-05 21:47:34 +0000256{
257 int
258 code;
259
cristybb503372010-05-27 20:51:26 +0000260 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000261 i;
262
cristy5d89f432010-05-30 00:00:26 +0000263 size_t
264 one;
265
cristy3ed852e2009-09-05 21:47:34 +0000266 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
267 (lzw_info->code_info.eof == MagickFalse))
268 {
269 ssize_t
270 count;
271
272 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
273 lzw_info->code_info.count-2];
274 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
275 lzw_info->code_info.count-1];
276 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
277 lzw_info->code_info.count=2;
278 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
279 lzw_info->code_info.count]);
280 if (count > 0)
281 lzw_info->code_info.count+=count;
282 else
283 lzw_info->code_info.eof=MagickTrue;
284 }
285 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
286 return(-1);
287 code=0;
cristy5d89f432010-05-30 00:00:26 +0000288 one=1;
cristybb503372010-05-27 20:51:26 +0000289 for (i=0; i < (ssize_t) bits; i++)
cristy3ed852e2009-09-05 21:47:34 +0000290 {
291 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
cristy5d89f432010-05-30 00:00:26 +0000292 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
cristy3ed852e2009-09-05 21:47:34 +0000293 lzw_info->code_info.bit++;
294 }
295 return(code);
296}
297
cristy15893a42010-11-20 18:57:15 +0000298static inline int PopLZWStack(LZWStack *stack_info)
cristy3ed852e2009-09-05 21:47:34 +0000299{
300 if (stack_info->index <= stack_info->codes)
301 return(-1);
302 stack_info->index--;
cristy15893a42010-11-20 18:57:15 +0000303 return((int) *stack_info->index);
cristy3ed852e2009-09-05 21:47:34 +0000304}
305
cristybb503372010-05-27 20:51:26 +0000306static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000307{
308 if (stack_info->index >= stack_info->top)
309 return;
310 *stack_info->index=value;
311 stack_info->index++;
312}
313
314static int ReadBlobLZWByte(LZWInfo *lzw_info)
315{
316 int
317 code;
318
319 ssize_t
320 count;
321
cristybb503372010-05-27 20:51:26 +0000322 size_t
cristy5d89f432010-05-30 00:00:26 +0000323 one,
cristy3ed852e2009-09-05 21:47:34 +0000324 value;
325
326 if (lzw_info->stack->index != lzw_info->stack->codes)
327 return(PopLZWStack(lzw_info->stack));
328 if (lzw_info->genesis != MagickFalse)
329 {
330 lzw_info->genesis=MagickFalse;
331 do
332 {
cristybb503372010-05-27 20:51:26 +0000333 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,
cristy3ed852e2009-09-05 21:47:34 +0000334 lzw_info->bits);
335 lzw_info->last_code=lzw_info->first_code;
336 } while (lzw_info->first_code == lzw_info->clear_code);
337 return((int) lzw_info->first_code);
338 }
339 code=GetNextLZWCode(lzw_info,lzw_info->bits);
340 if (code < 0)
341 return(code);
cristybb503372010-05-27 20:51:26 +0000342 if ((size_t) code == lzw_info->clear_code)
cristy3ed852e2009-09-05 21:47:34 +0000343 {
344 ResetLZWInfo(lzw_info);
345 return(ReadBlobLZWByte(lzw_info));
346 }
cristybb503372010-05-27 20:51:26 +0000347 if ((size_t) code == lzw_info->end_code)
cristy3ed852e2009-09-05 21:47:34 +0000348 return(-1);
cristybb503372010-05-27 20:51:26 +0000349 if ((size_t) code < lzw_info->slot)
350 value=(size_t) code;
cristy3ed852e2009-09-05 21:47:34 +0000351 else
352 {
353 PushLZWStack(lzw_info->stack,lzw_info->first_code);
354 value=lzw_info->last_code;
355 }
356 count=0;
357 while (value > lzw_info->maximum_data_value)
358 {
359 if ((size_t) count > MaximumLZWCode)
360 return(-1);
361 count++;
362 if ((size_t) value > MaximumLZWCode)
363 return(-1);
364 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
365 value=lzw_info->table[0][value];
366 }
367 lzw_info->first_code=lzw_info->table[1][value];
368 PushLZWStack(lzw_info->stack,lzw_info->first_code);
cristy5d89f432010-05-30 00:00:26 +0000369 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000370 if (lzw_info->slot < MaximumLZWCode)
371 {
372 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
373 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
374 lzw_info->slot++;
375 if ((lzw_info->slot >= lzw_info->maximum_code) &&
376 (lzw_info->bits < MaximumLZWBits))
377 {
378 lzw_info->bits++;
cristyeaedf062010-05-29 22:36:02 +0000379 lzw_info->maximum_code=one << lzw_info->bits;
cristy3ed852e2009-09-05 21:47:34 +0000380 }
381 }
cristybb503372010-05-27 20:51:26 +0000382 lzw_info->last_code=(size_t) code;
cristy3ed852e2009-09-05 21:47:34 +0000383 return(PopLZWStack(lzw_info->stack));
384}
385
cristybb503372010-05-27 20:51:26 +0000386static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
cristy3ed852e2009-09-05 21:47:34 +0000387{
388 ExceptionInfo
389 *exception;
390
391 IndexPacket
392 index;
393
394 int
395 c;
396
cristybb503372010-05-27 20:51:26 +0000397 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000398 offset,
399 y;
400
401 LZWInfo
402 *lzw_info;
403
404 unsigned char
405 data_size;
406
cristybb503372010-05-27 20:51:26 +0000407 size_t
cristy3ed852e2009-09-05 21:47:34 +0000408 pass;
409
410 /*
411 Allocate decoder tables.
412 */
413 assert(image != (Image *) NULL);
414 assert(image->signature == MagickSignature);
415 if (image->debug != MagickFalse)
416 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
417 data_size=(unsigned char) ReadBlobByte(image);
418 if (data_size > MaximumLZWBits)
419 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
420 lzw_info=AcquireLZWInfo(image,data_size);
421 if (lzw_info == (LZWInfo *) NULL)
422 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
423 image->filename);
424 exception=(&image->exception);
425 pass=0;
426 offset=0;
cristybb503372010-05-27 20:51:26 +0000427 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000428 {
429 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000430 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000431
cristybb503372010-05-27 20:51:26 +0000432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000433 x;
434
435 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000436 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000437
438 q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
439 if (q == (PixelPacket *) NULL)
440 break;
441 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000442 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +0000443 {
444 c=ReadBlobLZWByte(lzw_info);
445 if (c < 0)
446 break;
cristybb503372010-05-27 20:51:26 +0000447 index=ConstrainColormapIndex(image,(size_t) c);
448 q->red=image->colormap[(ssize_t) index].red;
449 q->green=image->colormap[(ssize_t) index].green;
450 q->blue=image->colormap[(ssize_t) index].blue;
451 q->opacity=(ssize_t) index == opacity ? (Quantum) TransparentOpacity :
cristy3ed852e2009-09-05 21:47:34 +0000452 (Quantum) OpaqueOpacity;
453 indexes[x]=index;
454 x++;
455 q++;
456 }
cristybb503372010-05-27 20:51:26 +0000457 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000458 break;
459 if (image->interlace == NoInterlace)
460 offset++;
461 else
462 switch (pass)
463 {
464 case 0:
465 default:
466 {
467 offset+=8;
cristybb503372010-05-27 20:51:26 +0000468 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000469 {
470 pass++;
471 offset=4;
472 }
473 break;
474 }
475 case 1:
476 {
477 offset+=8;
cristybb503372010-05-27 20:51:26 +0000478 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000479 {
480 pass++;
481 offset=2;
482 }
483 break;
484 }
485 case 2:
486 {
487 offset+=4;
cristybb503372010-05-27 20:51:26 +0000488 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000489 {
490 pass++;
491 offset=1;
492 }
493 break;
494 }
495 case 3:
496 {
497 offset+=2;
498 break;
499 }
500 }
501 if (SyncAuthenticPixels(image,exception) == MagickFalse)
502 break;
503 }
504 lzw_info=RelinquishLZWInfo(lzw_info);
cristybb503372010-05-27 20:51:26 +0000505 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000506 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
507 return(MagickTrue);
508}
509
510/*
511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512% %
513% %
514% %
515% E n c o d e I m a g e %
516% %
517% %
518% %
519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520%
521% EncodeImage compresses an image via GIF-coding.
522%
523% The format of the EncodeImage method is:
524%
525% MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristybb503372010-05-27 20:51:26 +0000526% const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000527%
528% A description of each parameter follows:
529%
530% o image_info: the image info.
531%
532% o image: the address of a structure of type Image.
533%
534% o data_size: The number of bits in the compressed packet.
535%
536*/
537static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristybb503372010-05-27 20:51:26 +0000538 const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000539{
cristy5d89f432010-05-30 00:00:26 +0000540#define MaxCode(number_bits) ((one << (number_bits))-1)
cristy3ed852e2009-09-05 21:47:34 +0000541#define MaxHashTable 5003
542#define MaxGIFBits 12UL
543#define MaxGIFTable (1UL << MaxGIFBits)
544#define GIFOutputCode(code) \
545{ \
546 /* \
547 Emit a code. \
548 */ \
549 if (bits > 0) \
550 datum|=(code) << bits; \
551 else \
552 datum=code; \
553 bits+=number_bits; \
554 while (bits >= 8) \
555 { \
556 /* \
557 Add a character to current packet. \
558 */ \
559 packet[length++]=(unsigned char) (datum & 0xff); \
560 if (length >= 254) \
561 { \
562 (void) WriteBlobByte(image,(unsigned char) length); \
563 (void) WriteBlob(image,length,packet); \
564 length=0; \
565 } \
566 datum>>=8; \
567 bits-=8; \
568 } \
569 if (free_code > max_code) \
570 { \
571 number_bits++; \
572 if (number_bits == MaxGIFBits) \
573 max_code=MaxGIFTable; \
574 else \
575 max_code=MaxCode(number_bits); \
576 } \
577}
578
579 IndexPacket
580 index;
581
cristybb503372010-05-27 20:51:26 +0000582 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000583 displacement,
584 offset,
585 k,
586 y;
587
cristybb503372010-05-27 20:51:26 +0000588 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000589 i;
590
591 size_t
cristy5d89f432010-05-30 00:00:26 +0000592 length,
593 one;
cristy3ed852e2009-09-05 21:47:34 +0000594
595 short
596 *hash_code,
597 *hash_prefix,
598 waiting_code;
599
600 unsigned char
601 *packet,
602 *hash_suffix;
603
cristybb503372010-05-27 20:51:26 +0000604 size_t
cristy3ed852e2009-09-05 21:47:34 +0000605 bits,
606 clear_code,
607 datum,
608 end_of_information_code,
609 free_code,
610 max_code,
611 next_pixel,
612 number_bits,
613 pass;
614
615 /*
616 Allocate encoder tables.
617 */
618 assert(image != (Image *) NULL);
cristy5d89f432010-05-30 00:00:26 +0000619 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000620 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
621 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
622 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
623 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
624 sizeof(*hash_suffix));
625 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
626 (hash_prefix == (short *) NULL) ||
627 (hash_suffix == (unsigned char *) NULL))
628 {
629 if (packet != (unsigned char *) NULL)
630 packet=(unsigned char *) RelinquishMagickMemory(packet);
631 if (hash_code != (short *) NULL)
632 hash_code=(short *) RelinquishMagickMemory(hash_code);
633 if (hash_prefix != (short *) NULL)
634 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
635 if (hash_suffix != (unsigned char *) NULL)
636 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
637 return(MagickFalse);
638 }
639 /*
640 Initialize GIF encoder.
641 */
642 number_bits=data_size;
643 max_code=MaxCode(number_bits);
cristy5d89f432010-05-30 00:00:26 +0000644 clear_code=((short) one << (data_size-1));
cristy3ed852e2009-09-05 21:47:34 +0000645 end_of_information_code=clear_code+1;
646 free_code=clear_code+2;
647 length=0;
648 datum=0;
649 bits=0;
650 for (i=0; i < MaxHashTable; i++)
651 hash_code[i]=0;
652 GIFOutputCode(clear_code);
653 /*
654 Encode pixels.
655 */
656 offset=0;
657 pass=0;
658 waiting_code=0;
cristybb503372010-05-27 20:51:26 +0000659 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000660 {
661 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000662 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000663
664 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000665 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000666
cristybb503372010-05-27 20:51:26 +0000667 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000668 x;
669
670 p=GetVirtualPixels(image,0,offset,image->columns,1,&image->exception);
671 if (p == (const PixelPacket *) NULL)
672 break;
673 indexes=GetVirtualIndexQueue(image);
674 if (y == 0)
675 waiting_code=(short) (*indexes);
cristy15893a42010-11-20 18:57:15 +0000676 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000677 {
678 /*
679 Probe hash table.
680 */
cristybb503372010-05-27 20:51:26 +0000681 index=(IndexPacket) ((size_t) indexes[x] & 0xff);
cristy3ed852e2009-09-05 21:47:34 +0000682 p++;
cristybb503372010-05-27 20:51:26 +0000683 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000684 if (k >= MaxHashTable)
685 k-=MaxHashTable;
686 next_pixel=MagickFalse;
687 displacement=1;
688 if (hash_code[k] > 0)
689 {
690 if ((hash_prefix[k] == waiting_code) &&
691 (hash_suffix[k] == (unsigned char) index))
692 {
693 waiting_code=hash_code[k];
694 continue;
695 }
696 if (k != 0)
697 displacement=MaxHashTable-k;
698 for ( ; ; )
699 {
700 k-=displacement;
701 if (k < 0)
702 k+=MaxHashTable;
703 if (hash_code[k] == 0)
704 break;
705 if ((hash_prefix[k] == waiting_code) &&
706 (hash_suffix[k] == (unsigned char) index))
707 {
708 waiting_code=hash_code[k];
709 next_pixel=MagickTrue;
710 break;
711 }
712 }
713 if (next_pixel == MagickTrue)
714 continue;
715 }
cristybb503372010-05-27 20:51:26 +0000716 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000717 if (free_code < MaxGIFTable)
718 {
719 hash_code[k]=(short) free_code++;
720 hash_prefix[k]=waiting_code;
721 hash_suffix[k]=(unsigned char) index;
722 }
723 else
724 {
725 /*
726 Fill the hash table with empty entries.
727 */
728 for (k=0; k < MaxHashTable; k++)
729 hash_code[k]=0;
730 /*
731 Reset compressor and issue a clear code.
732 */
733 free_code=clear_code+2;
734 GIFOutputCode(clear_code);
735 number_bits=data_size;
736 max_code=MaxCode(number_bits);
737 }
738 waiting_code=(short) index;
739 }
740 if (image_info->interlace == NoInterlace)
741 offset++;
742 else
743 switch (pass)
744 {
745 case 0:
746 default:
747 {
748 offset+=8;
cristybb503372010-05-27 20:51:26 +0000749 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000750 {
751 pass++;
752 offset=4;
753 }
754 break;
755 }
756 case 1:
757 {
758 offset+=8;
cristybb503372010-05-27 20:51:26 +0000759 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000760 {
761 pass++;
762 offset=2;
763 }
764 break;
765 }
766 case 2:
767 {
768 offset+=4;
cristybb503372010-05-27 20:51:26 +0000769 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000770 {
771 pass++;
772 offset=1;
773 }
774 break;
775 }
776 case 3:
777 {
778 offset+=2;
779 break;
780 }
781 }
782 }
783 /*
784 Flush out the buffered code.
785 */
cristybb503372010-05-27 20:51:26 +0000786 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000787 GIFOutputCode(end_of_information_code);
788 if (bits > 0)
789 {
790 /*
791 Add a character to current packet.
792 */
793 packet[length++]=(unsigned char) (datum & 0xff);
794 if (length >= 254)
795 {
796 (void) WriteBlobByte(image,(unsigned char) length);
797 (void) WriteBlob(image,length,packet);
798 length=0;
799 }
800 }
801 /*
802 Flush accumulated data.
803 */
804 if (length > 0)
805 {
806 (void) WriteBlobByte(image,(unsigned char) length);
807 (void) WriteBlob(image,length,packet);
808 }
809 /*
810 Free encoder memory.
811 */
812 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
813 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
814 hash_code=(short *) RelinquishMagickMemory(hash_code);
815 packet=(unsigned char *) RelinquishMagickMemory(packet);
816 return(MagickTrue);
817}
818
819/*
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821% %
822% %
823% %
824% I s G I F %
825% %
826% %
827% %
828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829%
830% IsGIF() returns MagickTrue if the image format type, identified by the
831% magick string, is GIF.
832%
833% The format of the IsGIF method is:
834%
835% MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
836%
837% A description of each parameter follows:
838%
839% o magick: compare image format pattern against these bytes.
840%
841% o length: Specifies the length of the magick string.
842%
843*/
844static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
845{
846 if (length < 4)
847 return(MagickFalse);
848 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
849 return(MagickTrue);
850 return(MagickFalse);
851}
852
853/*
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855% %
856% %
857% %
858+ R e a d B l o b B l o c k %
859% %
860% %
861% %
862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863%
864% ReadBlobBlock() reads data from the image file and returns it. The
865% amount of data is determined by first reading a count byte. The number
866% of bytes read is returned.
867%
868% The format of the ReadBlobBlock method is:
869%
870% size_t ReadBlobBlock(Image *image,unsigned char *data)
871%
872% A description of each parameter follows:
873%
874% o image: the image.
875%
876% o data: Specifies an area to place the information requested from
877% the file.
878%
879*/
880static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
881{
882 ssize_t
883 count;
884
885 unsigned char
886 block_count;
887
888 assert(image != (Image *) NULL);
889 assert(image->signature == MagickSignature);
890 assert(data != (unsigned char *) NULL);
891 count=ReadBlob(image,1,&block_count);
892 if (count != 1)
893 return(0);
894 return(ReadBlob(image,(size_t) block_count,data));
895}
896
897/*
898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899% %
900% %
901% %
902% R e a d G I F I m a g e %
903% %
904% %
905% %
906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907%
908% ReadGIFImage() reads a Compuserve Graphics image file and returns it.
909% It allocates the memory necessary for the new Image structure and returns a
910% pointer to the new image.
911%
912% The format of the ReadGIFImage method is:
913%
914% Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
915%
916% A description of each parameter follows:
917%
918% o image_info: the image info.
919%
920% o exception: return any errors or warnings in this structure.
921%
922*/
923
924static inline size_t MagickMax(const size_t x,const size_t y)
925{
926 if (x > y)
927 return(x);
928 return(y);
929}
930
931static inline size_t MagickMin(const size_t x,const size_t y)
932{
933 if (x < y)
934 return(x);
935 return(y);
936}
937
938static MagickBooleanType PingGIFImage(Image *image)
939{
940 unsigned char
941 buffer[256],
942 length,
943 data_size;
944
945 assert(image != (Image *) NULL);
946 assert(image->signature == MagickSignature);
947 if (image->debug != MagickFalse)
948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
949 if (ReadBlob(image,1,&data_size) != 1)
950 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
951 if (data_size > MaximumLZWBits)
952 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
953 if (ReadBlob(image,1,&length) != 1)
954 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
955 while (length != 0)
956 {
957 if (ReadBlob(image,length,buffer) != (ssize_t) length)
958 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
959 if (ReadBlob(image,1,&length) != 1)
960 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
961 }
962 return(MagickTrue);
963}
964
965static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
966{
967#define BitSet(byte,bit) (((byte) & (bit)) == (bit))
968#define LSBFirstOrder(x,y) (((y) << 8) | (x))
969
970 Image
971 *image;
972
973 int
974 number_extensionss=0;
975
cristybb503372010-05-27 20:51:26 +0000976 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000977 opacity;
978
979 MagickBooleanType
980 status;
981
982 RectangleInfo
983 page;
984
cristybb503372010-05-27 20:51:26 +0000985 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000986 i;
987
988 register unsigned char
989 *p;
990
991 ssize_t
992 count;
993
994 unsigned char
995 background,
996 c,
997 flag,
998 *global_colormap,
999 header[MaxTextExtent],
1000 magick[12];
1001
cristybb503372010-05-27 20:51:26 +00001002 size_t
cristy3ed852e2009-09-05 21:47:34 +00001003 delay,
1004 dispose,
1005 global_colors,
1006 image_count,
cristyeaedf062010-05-29 22:36:02 +00001007 iterations,
1008 one;
cristy3ed852e2009-09-05 21:47:34 +00001009
1010 /*
1011 Open image file.
1012 */
1013 assert(image_info != (const ImageInfo *) NULL);
1014 assert(image_info->signature == MagickSignature);
1015 if (image_info->debug != MagickFalse)
1016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1017 image_info->filename);
1018 assert(exception != (ExceptionInfo *) NULL);
1019 assert(exception->signature == MagickSignature);
1020 image=AcquireImage(image_info);
1021 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1022 if (status == MagickFalse)
1023 {
1024 image=DestroyImageList(image);
1025 return((Image *) NULL);
1026 }
1027 /*
1028 Determine if this a GIF file.
1029 */
1030 count=ReadBlob(image,6,magick);
1031 if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1032 (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1033 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1034 page.width=ReadBlobLSBShort(image);
1035 page.height=ReadBlobLSBShort(image);
1036 flag=(unsigned char) ReadBlobByte(image);
1037 background=(unsigned char) ReadBlobByte(image);
1038 c=(unsigned char) ReadBlobByte(image); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001039 one=1;
1040 global_colors=one << (((size_t) flag & 0x07)+1);
cristy3ed852e2009-09-05 21:47:34 +00001041 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1042 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1043 if (global_colormap == (unsigned char *) NULL)
1044 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1045 if (BitSet((int) flag,0x80) != 0)
1046 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1047 delay=0;
1048 dispose=0;
1049 iterations=1;
1050 opacity=(-1);
1051 image_count=0;
1052 for ( ; ; )
1053 {
1054 count=ReadBlob(image,1,&c);
1055 if (count != 1)
1056 break;
1057 if (c == (unsigned char) ';')
1058 break; /* terminator */
1059 if (c == (unsigned char) '!')
1060 {
1061 /*
1062 GIF Extension block.
1063 */
1064
1065 count=ReadBlob(image,1,&c);
1066 if (count != 1)
1067 {
1068 global_colormap=(unsigned char *) RelinquishMagickMemory(
1069 global_colormap);
1070 ThrowReaderException(CorruptImageError,
1071 "UnableToReadExtensionBlock");
1072 }
1073 switch (c)
1074 {
1075 case 0xf9:
1076 {
1077 /*
1078 Read graphics control extension.
1079 */
1080 while (ReadBlobBlock(image,header) != 0) ;
cristybb503372010-05-27 20:51:26 +00001081 dispose=(size_t) (header[0] >> 2);
1082 delay=(size_t) ((header[2] << 8) | header[1]);
1083 if ((ssize_t) (header[0] & 0x01) == 0x01)
1084 opacity=(ssize_t) header[3];
cristy3ed852e2009-09-05 21:47:34 +00001085 break;
1086 }
1087 case 0xfe:
1088 {
1089 char
1090 *comments;
1091
1092 /*
1093 Read comment extension.
1094 */
1095 comments=AcquireString((char *) NULL);
1096 for ( ; ; )
1097 {
1098 count=(ssize_t) ReadBlobBlock(image,header);
1099 if (count == 0)
1100 break;
1101 header[count]='\0';
1102 (void) ConcatenateString(&comments,(const char *) header);
1103 }
1104 (void) SetImageProperty(image,"comment",comments);
1105 comments=DestroyString(comments);
1106 break;
1107 }
1108 case 0xff:
1109 {
1110 /* Read GIF application extension */
1111
1112 MagickBooleanType
1113 loop;
1114
1115 /*
1116 Read Netscape Loop extension.
1117 */
1118 loop=MagickFalse;
1119 if (ReadBlobBlock(image,header) != 0)
1120 loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1121 MagickTrue : MagickFalse;
1122 if (loop != MagickFalse)
1123 {
1124 while (ReadBlobBlock(image,header) != 0)
cristybb503372010-05-27 20:51:26 +00001125 iterations=(size_t) ((header[2] << 8) | header[1]);
cristy3ed852e2009-09-05 21:47:34 +00001126 break;
1127 }
1128 else
1129 {
1130 char
1131 name[MaxTextExtent];
1132
1133 int
1134 block_length,
1135 info_length,
1136 reserved_length;
1137
1138 MagickBooleanType
1139 i8bim,
1140 icc,
1141 iptc;
1142
1143 StringInfo
1144 *profile;
1145
1146 unsigned char
1147 *info;
1148
1149 /*
1150 Store GIF application extension as a generic profile.
1151 */
1152 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1153 MagickTrue : MagickFalse;
1154 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1155 MagickTrue : MagickFalse;
1156 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1157 MagickTrue : MagickFalse;
1158 number_extensionss++;
1159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1160 " Reading GIF application extension");
1161 info=(unsigned char *) AcquireQuantumMemory(255UL,
1162 sizeof(*info));
1163 reserved_length=255;
1164 for (info_length=0; ; )
1165 {
1166 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1167 if (block_length == 0)
1168 break;
1169 info_length+=block_length;
1170 if (info_length > (reserved_length-255))
1171 {
1172 reserved_length+=4096;
1173 info=(unsigned char *) ResizeQuantumMemory(info,
1174 (size_t) reserved_length,sizeof(*info));
1175 }
1176 }
1177 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1178 (info_length+1),sizeof(*info));
1179 profile=AcquireStringInfo((size_t) info_length);
1180 SetStringInfoDatum(profile,(const unsigned char *) info);
1181 if (i8bim == MagickTrue)
1182 (void) CopyMagickString(name,"8bim",sizeof(name));
1183 else if (icc == MagickTrue)
1184 (void) CopyMagickString(name,"icc",sizeof(name));
1185 else if (iptc == MagickTrue)
1186 (void) CopyMagickString(name,"iptc",sizeof(name));
1187 else
1188 (void) FormatMagickString(name,sizeof(name),"gif:%.11s",
1189 header);
1190 (void) SetImageProfile(image,name,profile);
1191 info=(unsigned char *) RelinquishMagickMemory(info);
1192 profile=DestroyStringInfo(profile);
1193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1194 " profile name=%s",name);
1195 }
1196 break;
1197 }
1198 default:
1199 {
1200 while (ReadBlobBlock(image,header) != 0) ;
1201 break;
1202 }
1203 }
1204 }
1205 if (c != (unsigned char) ',')
1206 continue;
1207 if (image_count != 0)
1208 {
1209 /*
1210 Allocate next image structure.
1211 */
1212 AcquireNextImage(image_info,image);
1213 if (GetNextImageInList(image) == (Image *) NULL)
1214 {
1215 image=DestroyImageList(image);
1216 global_colormap=(unsigned char *) RelinquishMagickMemory(
1217 global_colormap);
1218 return((Image *) NULL);
1219 }
1220 image=SyncNextImageInList(image);
1221 }
1222 image_count++;
1223 /*
1224 Read image attributes.
1225 */
1226 image->storage_class=PseudoClass;
1227 image->compression=LZWCompression;
cristybb503372010-05-27 20:51:26 +00001228 page.x=(ssize_t) ReadBlobLSBShort(image);
1229 page.y=(ssize_t) ReadBlobLSBShort(image);
cristy3ed852e2009-09-05 21:47:34 +00001230 image->columns=ReadBlobLSBShort(image);
1231 image->rows=ReadBlobLSBShort(image);
1232 image->depth=8;
1233 flag=(unsigned char) ReadBlobByte(image);
1234 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace :
1235 NoInterlace;
1236 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors :
cristyeaedf062010-05-29 22:36:02 +00001237 one << ((size_t) (flag & 0x07)+1);
cristybb503372010-05-27 20:51:26 +00001238 if (opacity >= (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001239 opacity=(-1);
1240 image->page.width=page.width;
1241 image->page.height=page.height;
1242 image->page.y=page.y;
1243 image->page.x=page.x;
1244 image->delay=delay;
1245 image->ticks_per_second=100;
1246 image->dispose=(DisposeType) dispose;
1247 image->iterations=iterations;
1248 image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1249 delay=0;
1250 dispose=0;
1251 iterations=1;
1252 if ((image->columns == 0) || (image->rows == 0))
1253 {
1254 global_colormap=(unsigned char *) RelinquishMagickMemory(
1255 global_colormap);
1256 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1257 }
1258 /*
1259 Inititialize colormap.
1260 */
1261 if (AcquireImageColormap(image,image->colors) == MagickFalse)
1262 {
1263 global_colormap=(unsigned char *) RelinquishMagickMemory(
1264 global_colormap);
1265 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1266 }
1267 if (BitSet((int) flag,0x80) == 0)
1268 {
1269 /*
1270 Use global colormap.
1271 */
1272 p=global_colormap;
cristybb503372010-05-27 20:51:26 +00001273 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001274 {
1275 image->colormap[i].red=ScaleCharToQuantum(*p++);
1276 image->colormap[i].green=ScaleCharToQuantum(*p++);
1277 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1278 if (i == opacity)
1279 {
1280 image->colormap[i].opacity=(Quantum) TransparentOpacity;
1281 image->transparent_color=image->colormap[opacity];
1282 }
1283 }
1284 image->background_color=image->colormap[MagickMin(background,
1285 image->colors-1)];
1286 }
1287 else
1288 {
1289 unsigned char
1290 *colormap;
1291
1292 /*
1293 Read local colormap.
1294 */
1295 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1296 3*sizeof(*colormap));
1297 if (colormap == (unsigned char *) NULL)
1298 {
1299 global_colormap=(unsigned char *) RelinquishMagickMemory(
1300 global_colormap);
1301 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1302 }
1303 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1304 if (count != (ssize_t) (3*image->colors))
1305 {
1306 global_colormap=(unsigned char *) RelinquishMagickMemory(
1307 global_colormap);
1308 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1309 ThrowReaderException(CorruptImageError,
1310 "InsufficientImageDataInFile");
1311 }
1312 p=colormap;
cristybb503372010-05-27 20:51:26 +00001313 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001314 {
1315 image->colormap[i].red=ScaleCharToQuantum(*p++);
1316 image->colormap[i].green=ScaleCharToQuantum(*p++);
1317 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1318 if (i == opacity)
1319 image->colormap[i].opacity=(Quantum) TransparentOpacity;
1320 }
1321 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1322 }
1323 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1324 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1325 break;
1326 /*
1327 Decode image.
1328 */
1329 if (image_info->ping != MagickFalse)
1330 status=PingGIFImage(image);
1331 else
1332 status=DecodeImage(image,opacity);
1333 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1334 {
1335 global_colormap=(unsigned char *) RelinquishMagickMemory(
1336 global_colormap);
1337 ThrowReaderException(CorruptImageError,"CorruptImage");
1338 }
1339 if (image_info->number_scenes != 0)
1340 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1341 break;
1342 opacity=(-1);
1343 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1344 1,image->scene);
1345 if (status == MagickFalse)
1346 break;
1347 }
1348 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1349 if ((image->columns == 0) || (image->rows == 0))
1350 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1351 (void) CloseBlob(image);
1352 return(GetFirstImageInList(image));
1353}
1354
1355/*
1356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1357% %
1358% %
1359% %
1360% R e g i s t e r G I F I m a g e %
1361% %
1362% %
1363% %
1364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1365%
1366% RegisterGIFImage() adds properties for the GIF image format to
1367% the list of supported formats. The properties include the image format
1368% tag, a method to read and/or write the format, whether the format
1369% supports the saving of more than one frame to the same file or blob,
1370% whether the format supports native in-memory I/O, and a brief
1371% description of the format.
1372%
1373% The format of the RegisterGIFImage method is:
1374%
cristybb503372010-05-27 20:51:26 +00001375% size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001376%
1377*/
cristybb503372010-05-27 20:51:26 +00001378ModuleExport size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001379{
1380 MagickInfo
1381 *entry;
1382
1383 entry=SetMagickInfo("GIF");
1384 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1385 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1386 entry->magick=(IsImageFormatHandler *) IsGIF;
1387 entry->description=ConstantString("CompuServe graphics interchange format");
1388 entry->module=ConstantString("GIF");
1389 (void) RegisterMagickInfo(entry);
1390 entry=SetMagickInfo("GIF87");
1391 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1392 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1393 entry->magick=(IsImageFormatHandler *) IsGIF;
1394 entry->adjoin=MagickFalse;
1395 entry->description=ConstantString("CompuServe graphics interchange format");
1396 entry->version=ConstantString("version 87a");
1397 entry->module=ConstantString("GIF");
1398 (void) RegisterMagickInfo(entry);
1399 return(MagickImageCoderSignature);
1400}
1401
1402/*
1403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404% %
1405% %
1406% %
1407% U n r e g i s t e r G I F I m a g e %
1408% %
1409% %
1410% %
1411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412%
1413% UnregisterGIFImage() removes format registrations made by the
1414% GIF module from the list of supported formats.
1415%
1416% The format of the UnregisterGIFImage method is:
1417%
1418% UnregisterGIFImage(void)
1419%
1420*/
1421ModuleExport void UnregisterGIFImage(void)
1422{
1423 (void) UnregisterMagickInfo("GIF");
1424 (void) UnregisterMagickInfo("GIF87");
1425}
1426
1427/*
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429% %
1430% %
1431% %
1432% W r i t e G I F I m a g e %
1433% %
1434% %
1435% %
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437%
1438% WriteGIFImage() writes an image to a file in the Compuserve Graphics
1439% image format.
1440%
1441% The format of the WriteGIFImage method is:
1442%
1443% MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1444%
1445% A description of each parameter follows.
1446%
1447% o image_info: the image info.
1448%
1449% o image: The image.
1450%
1451*/
1452static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1453{
1454 Image
1455 *next_image;
1456
1457 int
1458 c;
1459
cristybb503372010-05-27 20:51:26 +00001460 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001461 j,
1462 opacity;
1463
1464 ImageInfo
1465 *write_info;
1466
1467 InterlaceType
1468 interlace;
1469
1470 MagickBooleanType
1471 status;
1472
1473 MagickOffsetType
1474 scene;
1475
1476 RectangleInfo
1477 page;
1478
cristybb503372010-05-27 20:51:26 +00001479 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001480 i;
1481
1482 register unsigned char
1483 *q;
1484
1485 size_t
1486 length;
1487
1488 unsigned char
1489 *colormap,
1490 *global_colormap;
1491
cristybb503372010-05-27 20:51:26 +00001492 size_t
cristy3ed852e2009-09-05 21:47:34 +00001493 bits_per_pixel,
cristy5d89f432010-05-30 00:00:26 +00001494 delay,
1495 one;
cristy3ed852e2009-09-05 21:47:34 +00001496
1497 /*
1498 Open output image file.
1499 */
1500 assert(image_info != (const ImageInfo *) NULL);
1501 assert(image_info->signature == MagickSignature);
1502 assert(image != (Image *) NULL);
1503 assert(image->signature == MagickSignature);
1504 if (image->debug != MagickFalse)
1505 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1506 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1507 if (status == MagickFalse)
1508 return(status);
1509 /*
1510 Allocate colormap.
1511 */
1512 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1513 sizeof(*global_colormap));
1514 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1515 if ((global_colormap == (unsigned char *) NULL) ||
1516 (colormap == (unsigned char *) NULL))
1517 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1518 for (i=0; i < 768; i++)
1519 colormap[i]=(unsigned char) 0;
1520 /*
1521 Write GIF header.
1522 */
1523 write_info=CloneImageInfo(image_info);
1524 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1525 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1526 else
1527 {
1528 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1529 write_info->adjoin=MagickFalse;
1530 }
1531 /*
1532 Determine image bounding box.
1533 */
1534 page.width=image->columns;
1535 page.height=image->rows;
1536 page.x=0;
1537 page.y=0;
1538 if (write_info->adjoin != MagickFalse)
1539 for (next_image=image; next_image != (Image *) NULL; )
1540 {
1541 page.x=next_image->page.x;
1542 page.y=next_image->page.y;
1543 if ((next_image->page.width+page.x) > page.width)
1544 page.width=next_image->page.width+page.x;
1545 if ((next_image->page.height+page.y) > page.height)
1546 page.height=next_image->page.height+page.y;
1547 next_image=GetNextImageInList(next_image);
1548 }
1549 page.x=image->page.x;
1550 page.y=image->page.y;
1551 if ((image->page.width != 0) && (image->page.height != 0))
1552 page=image->page;
1553 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1554 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1555 /*
1556 Write images to file.
1557 */
1558 interlace=write_info->interlace;
1559 if ((write_info->adjoin != MagickFalse) &&
1560 (GetNextImageInList(image) != (Image *) NULL))
1561 interlace=NoInterlace;
1562 scene=0;
cristy5d89f432010-05-30 00:00:26 +00001563 one=1;
cristy3ed852e2009-09-05 21:47:34 +00001564 do
1565 {
1566 if (image->colorspace != RGBColorspace)
1567 (void) TransformImageColorspace(image,RGBColorspace);
1568 opacity=(-1);
1569 if (IsOpaqueImage(image,&image->exception) != MagickFalse)
1570 {
1571 if ((image->storage_class == DirectClass) || (image->colors > 256))
1572 (void) SetImageType(image,PaletteType);
1573 }
1574 else
1575 {
1576 MagickRealType
1577 alpha,
1578 beta;
1579
1580 /*
1581 Identify transparent colormap index.
1582 */
1583 if ((image->storage_class == DirectClass) || (image->colors > 256))
1584 (void) SetImageType(image,PaletteBilevelMatteType);
cristybb503372010-05-27 20:51:26 +00001585 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001586 if (image->colormap[i].opacity != OpaqueOpacity)
1587 {
1588 if (opacity < 0)
1589 {
1590 opacity=i;
1591 continue;
1592 }
1593 alpha=(MagickRealType) TransparentOpacity-(MagickRealType)
1594 image->colormap[i].opacity;
1595 beta=(MagickRealType) TransparentOpacity-(MagickRealType)
1596 image->colormap[opacity].opacity;
1597 if (alpha < beta)
1598 opacity=i;
1599 }
1600 if (opacity == -1)
1601 {
1602 (void) SetImageType(image,PaletteBilevelMatteType);
cristybb503372010-05-27 20:51:26 +00001603 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001604 if (image->colormap[i].opacity != OpaqueOpacity)
1605 {
1606 if (opacity < 0)
1607 {
1608 opacity=i;
1609 continue;
1610 }
1611 alpha=(Quantum) TransparentOpacity-(MagickRealType)
1612 image->colormap[i].opacity;
1613 beta=(Quantum) TransparentOpacity-(MagickRealType)
1614 image->colormap[opacity].opacity;
1615 if (alpha < beta)
1616 opacity=i;
1617 }
1618 }
1619 if (opacity >= 0)
1620 {
1621 image->colormap[opacity].red=image->transparent_color.red;
1622 image->colormap[opacity].green=image->transparent_color.green;
1623 image->colormap[opacity].blue=image->transparent_color.blue;
1624 }
1625 }
1626 if ((image->storage_class == DirectClass) || (image->colors > 256))
1627 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1628 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
cristy5d89f432010-05-30 00:00:26 +00001629 if ((one << bits_per_pixel) >= image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001630 break;
1631 q=colormap;
cristybb503372010-05-27 20:51:26 +00001632 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001633 {
1634 *q++=ScaleQuantumToChar(image->colormap[i].red);
1635 *q++=ScaleQuantumToChar(image->colormap[i].green);
1636 *q++=ScaleQuantumToChar(image->colormap[i].blue);
1637 }
cristy5d89f432010-05-30 00:00:26 +00001638 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00001639 {
1640 *q++=(unsigned char) 0x0;
1641 *q++=(unsigned char) 0x0;
1642 *q++=(unsigned char) 0x0;
1643 }
1644 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1645 (write_info->adjoin == MagickFalse))
1646 {
1647 /*
1648 Write global colormap.
1649 */
1650 c=0x80;
1651 c|=(8-1) << 4; /* color resolution */
1652 c|=(bits_per_pixel-1); /* size of global colormap */
1653 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001654 for (j=0; j < (ssize_t) image->colors; j++)
cristy3ed852e2009-09-05 21:47:34 +00001655 if (IsColorEqual(&image->background_color,image->colormap+j))
1656 break;
1657 (void) WriteBlobByte(image,(unsigned char)
cristybb503372010-05-27 20:51:26 +00001658 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
cristy3ed852e2009-09-05 21:47:34 +00001659 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001660 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001661 (void) WriteBlob(image,length,colormap);
1662 for (j=0; j < 768; j++)
1663 global_colormap[j]=colormap[j];
1664 }
1665 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1666 {
1667 /*
1668 Write graphics control extension.
1669 */
1670 (void) WriteBlobByte(image,(unsigned char) 0x21);
1671 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1672 (void) WriteBlobByte(image,(unsigned char) 0x04);
1673 c=image->dispose << 2;
1674 if (opacity >= 0)
1675 c|=0x01;
1676 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001677 delay=(size_t) (100*image->delay/MagickMax((size_t)
cristy3ed852e2009-09-05 21:47:34 +00001678 image->ticks_per_second,1));
1679 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1680 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1681 0));
1682 (void) WriteBlobByte(image,(unsigned char) 0x00);
1683 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1684 (GetImageProperty(image,"comment") != (const char *) NULL))
1685 {
1686 const char
1687 *value;
1688
1689 register const char
1690 *p;
1691
1692 size_t
1693 count;
1694
1695 /*
1696 Write Comment extension.
1697 */
1698 (void) WriteBlobByte(image,(unsigned char) 0x21);
1699 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1700 value=GetImageProperty(image,"comment");
1701 p=value;
1702 while (strlen(p) != 0)
1703 {
1704 count=MagickMin(strlen(p),255);
1705 (void) WriteBlobByte(image,(unsigned char) count);
cristybb503372010-05-27 20:51:26 +00001706 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00001707 (void) WriteBlobByte(image,(unsigned char) *p++);
1708 }
1709 (void) WriteBlobByte(image,(unsigned char) 0x00);
1710 }
1711 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1712 (GetNextImageInList(image) != (Image *) NULL) &&
1713 (image->iterations != 1))
1714 {
1715 /*
1716 Write Netscape Loop extension.
1717 */
1718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1719 " Writing GIF Extension %s","NETSCAPE2.0");
1720 (void) WriteBlobByte(image,(unsigned char) 0x21);
1721 (void) WriteBlobByte(image,(unsigned char) 0xff);
1722 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1723 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1724 (void) WriteBlobByte(image,(unsigned char) 0x03);
1725 (void) WriteBlobByte(image,(unsigned char) 0x01);
1726 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1727 (void) WriteBlobByte(image,(unsigned char) 0x00);
1728 }
1729 ResetImageProfileIterator(image);
1730 for ( ; ; )
1731 {
1732 char
1733 *name;
1734
1735 const StringInfo
1736 *profile;
1737
1738 name=GetNextImageProfile(image);
1739 if (name == (const char *) NULL)
1740 break;
1741 profile=GetImageProfile(image,name);
1742 if (profile != (StringInfo *) NULL)
1743 {
1744 if ((LocaleCompare(name,"ICC") == 0) ||
1745 (LocaleCompare(name,"ICM") == 0) ||
1746 (LocaleCompare(name,"IPTC") == 0) ||
1747 (LocaleCompare(name,"8BIM") == 0) ||
1748 (LocaleNCompare(name,"gif:",4) == 0))
1749 {
1750 size_t
1751 length;
1752
1753 ssize_t
1754 offset;
1755
1756 unsigned char
1757 *datum;
1758
1759 datum=GetStringInfoDatum(profile);
1760 length=GetStringInfoLength(profile);
1761 (void) WriteBlobByte(image,(unsigned char) 0x21);
1762 (void) WriteBlobByte(image,(unsigned char) 0xff);
1763 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1764 if ((LocaleCompare(name,"ICC") == 0) ||
1765 (LocaleCompare(name,"ICM") == 0))
1766 {
1767 /*
1768 Write ICC extension.
1769 */
1770 (void) WriteBlob(image,11,(unsigned char *)"ICCRGBG1012");
1771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1772 " Writing GIF Extension %s","ICCRGBG1012");
1773 }
1774 else
1775 if ((LocaleCompare(name,"IPTC") == 0))
1776 {
1777 /*
1778 write IPTC extension.
1779 */
1780 (void) WriteBlob(image,11,(unsigned char *)"MGKIPTC0000");
1781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782 " Writing GIF Extension %s","MGKIPTC0000");
1783 }
1784 else
1785 if ((LocaleCompare(name,"8BIM") == 0))
1786 {
1787 /*
1788 Write 8BIM extension>
1789 */
1790 (void) WriteBlob(image,11,(unsigned char *)
1791 "MGK8BIM0000");
1792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1793 " Writing GIF Extension %s","MGK8BIM0000");
1794 }
1795 else
1796 {
1797 char
1798 extension[MaxTextExtent];
1799
1800 /* write generic extension */
1801 (void) CopyMagickString(extension,name+4,
1802 sizeof(extension));
1803 (void) WriteBlob(image,11,(unsigned char *) extension);
1804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1805 " Writing GIF Extension %s",name);
1806 }
1807 offset=0;
1808 while ((ssize_t) length > offset)
1809 {
1810 size_t
1811 block_length;
1812
1813 if ((length-offset) < 255)
1814 block_length=length-offset;
1815 else
1816 block_length=255;
1817 (void) WriteBlobByte(image,(unsigned char) block_length);
1818 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1819 offset+=(ssize_t) block_length;
1820 }
1821 (void) WriteBlobByte(image,(unsigned char) 0x00);
1822 }
1823 }
1824 }
1825 }
1826 (void) WriteBlobByte(image,','); /* image separator */
1827 /*
1828 Write the image header.
1829 */
1830 page.x=image->page.x;
1831 page.y=image->page.y;
1832 if ((image->page.width != 0) && (image->page.height != 0))
1833 page=image->page;
1834 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1835 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1836 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1837 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1838 c=0x00;
1839 if (interlace != NoInterlace)
1840 c|=0x40; /* pixel data is interlaced */
cristybb503372010-05-27 20:51:26 +00001841 for (j=0; j < (ssize_t) (3*image->colors); j++)
cristy3ed852e2009-09-05 21:47:34 +00001842 if (colormap[j] != global_colormap[j])
1843 break;
cristybb503372010-05-27 20:51:26 +00001844 if (j == (ssize_t) (3*image->colors))
cristy3ed852e2009-09-05 21:47:34 +00001845 (void) WriteBlobByte(image,(unsigned char) c);
1846 else
1847 {
1848 c|=0x80;
1849 c|=(bits_per_pixel-1); /* size of local colormap */
1850 (void) WriteBlobByte(image,(unsigned char) c);
cristy5d89f432010-05-30 00:00:26 +00001851 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001852 (void) WriteBlob(image,length,colormap);
1853 }
1854 /*
1855 Write the image data.
1856 */
1857 c=(int) MagickMax(bits_per_pixel,2);
1858 (void) WriteBlobByte(image,(unsigned char) c);
cristy5d89f432010-05-30 00:00:26 +00001859 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1);
cristy3ed852e2009-09-05 21:47:34 +00001860 if (status == MagickFalse)
1861 {
1862 global_colormap=(unsigned char *) RelinquishMagickMemory(
1863 global_colormap);
1864 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1865 write_info=DestroyImageInfo(write_info);
1866 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1867 }
1868 (void) WriteBlobByte(image,(unsigned char) 0x00);
1869 if (GetNextImageInList(image) == (Image *) NULL)
1870 break;
1871 image=SyncNextImageInList(image);
1872 scene++;
1873 status=SetImageProgress(image,SaveImagesTag,scene,
1874 GetImageListLength(image));
1875 if (status == MagickFalse)
1876 break;
1877 } while (write_info->adjoin != MagickFalse);
1878 (void) WriteBlobByte(image,';'); /* terminator */
1879 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1880 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1881 write_info=DestroyImageInfo(write_info);
1882 (void) CloseBlob(image);
1883 return(MagickTrue);
1884}