blob: 8af12bfe081ac14df42c077c5b9fc5192fe69e8f [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colormap-private.h"
51#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000052#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000053#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/image.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/list.h"
58#include "MagickCore/profile.h"
59#include "MagickCore/magick.h"
60#include "MagickCore/memory_.h"
61#include "MagickCore/monitor.h"
62#include "MagickCore/monitor-private.h"
63#include "MagickCore/option.h"
64#include "MagickCore/pixel.h"
65#include "MagickCore/pixel-accessor.h"
66#include "MagickCore/property.h"
67#include "MagickCore/quantize.h"
68#include "MagickCore/quantum-private.h"
69#include "MagickCore/static.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000072
73/*
74 Define declarations.
75*/
76#define MaximumLZWBits 12
77#define MaximumLZWCode (1UL << MaximumLZWBits)
78
79/*
80 Typdef declarations.
81*/
82typedef struct _LZWCodeInfo
83{
84 unsigned char
85 buffer[280];
86
cristybb503372010-05-27 20:51:26 +000087 size_t
cristy3ed852e2009-09-05 21:47:34 +000088 count,
89 bit;
90
91 MagickBooleanType
92 eof;
93} LZWCodeInfo;
94
95typedef struct _LZWStack
96{
cristybb503372010-05-27 20:51:26 +000097 size_t
cristy3ed852e2009-09-05 21:47:34 +000098 *codes,
99 *index,
100 *top;
101} LZWStack;
102
103typedef struct _LZWInfo
104{
105 Image
106 *image;
107
108 LZWStack
109 *stack;
110
111 MagickBooleanType
112 genesis;
113
cristybb503372010-05-27 20:51:26 +0000114 size_t
cristy3ed852e2009-09-05 21:47:34 +0000115 data_size,
116 maximum_data_value,
117 clear_code,
118 end_code,
119 bits,
120 first_code,
121 last_code,
122 maximum_code,
123 slot,
124 *table[2];
125
126 LZWCodeInfo
127 code_info;
128} LZWInfo;
129
130/*
131 Forward declarations.
132*/
133static inline int
cristybb503372010-05-27 20:51:26 +0000134 GetNextLZWCode(LZWInfo *,const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000135
136static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000137 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000138
139static ssize_t
140 ReadBlobBlock(Image *,unsigned char *);
141
142/*
143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144% %
145% %
146% %
147% D e c o d e I m a g e %
148% %
149% %
150% %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153% DecodeImage uncompresses an image via GIF-coding.
154%
155% The format of the DecodeImage method is:
156%
cristybb503372010-05-27 20:51:26 +0000157% MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
cristy3ed852e2009-09-05 21:47:34 +0000158%
159% A description of each parameter follows:
160%
161% o image: the address of a structure of type Image.
162%
163% o opacity: The colormap index associated with the transparent color.
164%
165*/
166
167static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
168{
cristybb503372010-05-27 20:51:26 +0000169 if (lzw_info->table[0] != (size_t *) NULL)
170 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000171 lzw_info->table[0]);
cristybb503372010-05-27 20:51:26 +0000172 if (lzw_info->table[1] != (size_t *) NULL)
173 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000174 lzw_info->table[1]);
175 if (lzw_info->stack != (LZWStack *) NULL)
176 {
cristybb503372010-05-27 20:51:26 +0000177 if (lzw_info->stack->codes != (size_t *) NULL)
178 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000179 lzw_info->stack->codes);
180 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
181 }
182 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
183 return((LZWInfo *) NULL);
184}
185
186static inline void ResetLZWInfo(LZWInfo *lzw_info)
187{
cristyeaedf062010-05-29 22:36:02 +0000188 size_t
189 one;
190
cristy3ed852e2009-09-05 21:47:34 +0000191 lzw_info->bits=lzw_info->data_size+1;
cristy5d89f432010-05-30 00:00:26 +0000192 one=1;
cristyeaedf062010-05-29 22:36:02 +0000193 lzw_info->maximum_code=one << lzw_info->bits;
cristy3ed852e2009-09-05 21:47:34 +0000194 lzw_info->slot=lzw_info->maximum_data_value+3;
195 lzw_info->genesis=MagickTrue;
196}
197
cristybb503372010-05-27 20:51:26 +0000198static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000199{
200 LZWInfo
201 *lzw_info;
202
cristybb503372010-05-27 20:51:26 +0000203 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000204 i;
205
cristy5d89f432010-05-30 00:00:26 +0000206 size_t
207 one;
208
cristy73bd4a52010-10-05 11:24:23 +0000209 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
cristy3ed852e2009-09-05 21:47:34 +0000210 if (lzw_info == (LZWInfo *) NULL)
211 return((LZWInfo *) NULL);
212 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
213 lzw_info->image=image;
214 lzw_info->data_size=data_size;
cristy5d89f432010-05-30 00:00:26 +0000215 one=1;
216 lzw_info->maximum_data_value=(one << data_size)-1;
cristy3ed852e2009-09-05 21:47:34 +0000217 lzw_info->clear_code=lzw_info->maximum_data_value+1;
218 lzw_info->end_code=lzw_info->maximum_data_value+2;
cristybb503372010-05-27 20:51:26 +0000219 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
cristy3ed852e2009-09-05 21:47:34 +0000220 sizeof(*lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000221 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
cristy3ed852e2009-09-05 21:47:34 +0000222 sizeof(*lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000223 if ((lzw_info->table[0] == (size_t *) NULL) ||
224 (lzw_info->table[1] == (size_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000225 {
226 lzw_info=RelinquishLZWInfo(lzw_info);
227 return((LZWInfo *) NULL);
228 }
cristybb503372010-05-27 20:51:26 +0000229 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
cristy3ed852e2009-09-05 21:47:34 +0000230 {
231 lzw_info->table[0][i]=0;
cristybb503372010-05-27 20:51:26 +0000232 lzw_info->table[1][i]=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +0000233 }
234 ResetLZWInfo(lzw_info);
235 lzw_info->code_info.buffer[0]='\0';
236 lzw_info->code_info.buffer[1]='\0';
237 lzw_info->code_info.count=2;
238 lzw_info->code_info.bit=8*lzw_info->code_info.count;
239 lzw_info->code_info.eof=MagickFalse;
240 lzw_info->genesis=MagickTrue;
cristy73bd4a52010-10-05 11:24:23 +0000241 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
cristy3ed852e2009-09-05 21:47:34 +0000242 if (lzw_info->stack == (LZWStack *) NULL)
243 {
244 lzw_info=RelinquishLZWInfo(lzw_info);
245 return((LZWInfo *) NULL);
246 }
cristybb503372010-05-27 20:51:26 +0000247 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
cristy3ed852e2009-09-05 21:47:34 +0000248 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
cristybb503372010-05-27 20:51:26 +0000249 if (lzw_info->stack->codes == (size_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000250 {
251 lzw_info=RelinquishLZWInfo(lzw_info);
252 return((LZWInfo *) NULL);
253 }
254 lzw_info->stack->index=lzw_info->stack->codes;
255 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
256 return(lzw_info);
257}
258
cristybb503372010-05-27 20:51:26 +0000259static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
cristy3ed852e2009-09-05 21:47:34 +0000260{
261 int
262 code;
263
cristybb503372010-05-27 20:51:26 +0000264 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000265 i;
266
cristy5d89f432010-05-30 00:00:26 +0000267 size_t
268 one;
269
cristy3ed852e2009-09-05 21:47:34 +0000270 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
271 (lzw_info->code_info.eof == MagickFalse))
272 {
273 ssize_t
274 count;
275
276 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
277 lzw_info->code_info.count-2];
278 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
279 lzw_info->code_info.count-1];
280 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
281 lzw_info->code_info.count=2;
282 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
283 lzw_info->code_info.count]);
284 if (count > 0)
285 lzw_info->code_info.count+=count;
286 else
287 lzw_info->code_info.eof=MagickTrue;
288 }
289 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
290 return(-1);
291 code=0;
cristy5d89f432010-05-30 00:00:26 +0000292 one=1;
cristybb503372010-05-27 20:51:26 +0000293 for (i=0; i < (ssize_t) bits; i++)
cristy3ed852e2009-09-05 21:47:34 +0000294 {
295 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
cristy5d89f432010-05-30 00:00:26 +0000296 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
cristy3ed852e2009-09-05 21:47:34 +0000297 lzw_info->code_info.bit++;
298 }
299 return(code);
300}
301
cristy15893a42010-11-20 18:57:15 +0000302static inline int PopLZWStack(LZWStack *stack_info)
cristy3ed852e2009-09-05 21:47:34 +0000303{
304 if (stack_info->index <= stack_info->codes)
305 return(-1);
306 stack_info->index--;
cristy15893a42010-11-20 18:57:15 +0000307 return((int) *stack_info->index);
cristy3ed852e2009-09-05 21:47:34 +0000308}
309
cristybb503372010-05-27 20:51:26 +0000310static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000311{
312 if (stack_info->index >= stack_info->top)
313 return;
314 *stack_info->index=value;
315 stack_info->index++;
316}
317
318static int ReadBlobLZWByte(LZWInfo *lzw_info)
319{
320 int
321 code;
322
323 ssize_t
324 count;
325
cristybb503372010-05-27 20:51:26 +0000326 size_t
cristy5d89f432010-05-30 00:00:26 +0000327 one,
cristy3ed852e2009-09-05 21:47:34 +0000328 value;
329
330 if (lzw_info->stack->index != lzw_info->stack->codes)
331 return(PopLZWStack(lzw_info->stack));
332 if (lzw_info->genesis != MagickFalse)
333 {
334 lzw_info->genesis=MagickFalse;
335 do
336 {
cristybb503372010-05-27 20:51:26 +0000337 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,
cristy3ed852e2009-09-05 21:47:34 +0000338 lzw_info->bits);
339 lzw_info->last_code=lzw_info->first_code;
340 } while (lzw_info->first_code == lzw_info->clear_code);
341 return((int) lzw_info->first_code);
342 }
343 code=GetNextLZWCode(lzw_info,lzw_info->bits);
344 if (code < 0)
345 return(code);
cristybb503372010-05-27 20:51:26 +0000346 if ((size_t) code == lzw_info->clear_code)
cristy3ed852e2009-09-05 21:47:34 +0000347 {
348 ResetLZWInfo(lzw_info);
349 return(ReadBlobLZWByte(lzw_info));
350 }
cristybb503372010-05-27 20:51:26 +0000351 if ((size_t) code == lzw_info->end_code)
cristy3ed852e2009-09-05 21:47:34 +0000352 return(-1);
cristybb503372010-05-27 20:51:26 +0000353 if ((size_t) code < lzw_info->slot)
354 value=(size_t) code;
cristy3ed852e2009-09-05 21:47:34 +0000355 else
356 {
357 PushLZWStack(lzw_info->stack,lzw_info->first_code);
358 value=lzw_info->last_code;
359 }
360 count=0;
361 while (value > lzw_info->maximum_data_value)
362 {
363 if ((size_t) count > MaximumLZWCode)
364 return(-1);
365 count++;
366 if ((size_t) value > MaximumLZWCode)
367 return(-1);
368 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
369 value=lzw_info->table[0][value];
370 }
371 lzw_info->first_code=lzw_info->table[1][value];
372 PushLZWStack(lzw_info->stack,lzw_info->first_code);
cristy5d89f432010-05-30 00:00:26 +0000373 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000374 if (lzw_info->slot < MaximumLZWCode)
375 {
376 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
377 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
378 lzw_info->slot++;
379 if ((lzw_info->slot >= lzw_info->maximum_code) &&
380 (lzw_info->bits < MaximumLZWBits))
381 {
382 lzw_info->bits++;
cristyeaedf062010-05-29 22:36:02 +0000383 lzw_info->maximum_code=one << lzw_info->bits;
cristy3ed852e2009-09-05 21:47:34 +0000384 }
385 }
cristybb503372010-05-27 20:51:26 +0000386 lzw_info->last_code=(size_t) code;
cristy3ed852e2009-09-05 21:47:34 +0000387 return(PopLZWStack(lzw_info->stack));
388}
389
cristy018f07f2011-09-04 21:15:19 +0000390static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity,
391 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000392{
cristy3ed852e2009-09-05 21:47:34 +0000393 int
394 c;
395
cristy4c08aed2011-07-01 19:47:50 +0000396 LZWInfo
397 *lzw_info;
398
399 Quantum
400 index;
401
402 size_t
403 pass;
404
cristybb503372010-05-27 20:51:26 +0000405 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 offset,
407 y;
408
cristy3ed852e2009-09-05 21:47:34 +0000409 unsigned char
410 data_size;
411
cristy3ed852e2009-09-05 21:47:34 +0000412 /*
413 Allocate decoder tables.
414 */
415 assert(image != (Image *) NULL);
416 assert(image->signature == MagickSignature);
417 if (image->debug != MagickFalse)
418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
419 data_size=(unsigned char) ReadBlobByte(image);
420 if (data_size > MaximumLZWBits)
421 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
422 lzw_info=AcquireLZWInfo(image,data_size);
423 if (lzw_info == (LZWInfo *) NULL)
424 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
425 image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000426 pass=0;
427 offset=0;
cristybb503372010-05-27 20:51:26 +0000428 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000429 {
cristybb503372010-05-27 20:51:26 +0000430 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000431 x;
432
cristy4c08aed2011-07-01 19:47:50 +0000433 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000434 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000435
436 q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000437 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000438 break;
cristybb503372010-05-27 20:51:26 +0000439 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +0000440 {
441 c=ReadBlobLZWByte(lzw_info);
442 if (c < 0)
443 break;
cristyc82a27b2011-10-21 01:07:16 +0000444 index=ConstrainColormapIndex(image,(size_t) c,exception);
cristy4c08aed2011-07-01 19:47:50 +0000445 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +0000446 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy4c08aed2011-07-01 19:47:50 +0000447 SetPixelAlpha(image,(ssize_t) index == opacity ? TransparentAlpha :
448 OpaqueAlpha,q);
cristy3ed852e2009-09-05 21:47:34 +0000449 x++;
cristyed231572011-07-14 02:18:59 +0000450 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000451 }
cristybb503372010-05-27 20:51:26 +0000452 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000453 break;
454 if (image->interlace == NoInterlace)
455 offset++;
456 else
457 switch (pass)
458 {
459 case 0:
460 default:
461 {
462 offset+=8;
cristybb503372010-05-27 20:51:26 +0000463 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000464 {
465 pass++;
466 offset=4;
467 }
468 break;
469 }
470 case 1:
471 {
472 offset+=8;
cristybb503372010-05-27 20:51:26 +0000473 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000474 {
475 pass++;
476 offset=2;
477 }
478 break;
479 }
480 case 2:
481 {
482 offset+=4;
cristybb503372010-05-27 20:51:26 +0000483 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000484 {
485 pass++;
486 offset=1;
487 }
488 break;
489 }
490 case 3:
491 {
492 offset+=2;
493 break;
494 }
495 }
496 if (SyncAuthenticPixels(image,exception) == MagickFalse)
497 break;
498 }
499 lzw_info=RelinquishLZWInfo(lzw_info);
cristybb503372010-05-27 20:51:26 +0000500 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000501 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
502 return(MagickTrue);
503}
504
505/*
506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507% %
508% %
509% %
510% E n c o d e I m a g e %
511% %
512% %
513% %
514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
515%
516% EncodeImage compresses an image via GIF-coding.
517%
518% The format of the EncodeImage method is:
519%
520% MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristybb503372010-05-27 20:51:26 +0000521% const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000522%
523% A description of each parameter follows:
524%
525% o image_info: the image info.
526%
527% o image: the address of a structure of type Image.
528%
529% o data_size: The number of bits in the compressed packet.
530%
531*/
532static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristyc82a27b2011-10-21 01:07:16 +0000533 const size_t data_size,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000534{
cristy5d89f432010-05-30 00:00:26 +0000535#define MaxCode(number_bits) ((one << (number_bits))-1)
cristy3ed852e2009-09-05 21:47:34 +0000536#define MaxHashTable 5003
537#define MaxGIFBits 12UL
538#define MaxGIFTable (1UL << MaxGIFBits)
539#define GIFOutputCode(code) \
540{ \
541 /* \
542 Emit a code. \
543 */ \
544 if (bits > 0) \
545 datum|=(code) << bits; \
546 else \
547 datum=code; \
548 bits+=number_bits; \
549 while (bits >= 8) \
550 { \
551 /* \
552 Add a character to current packet. \
553 */ \
554 packet[length++]=(unsigned char) (datum & 0xff); \
555 if (length >= 254) \
556 { \
557 (void) WriteBlobByte(image,(unsigned char) length); \
558 (void) WriteBlob(image,length,packet); \
559 length=0; \
560 } \
561 datum>>=8; \
562 bits-=8; \
563 } \
564 if (free_code > max_code) \
565 { \
566 number_bits++; \
567 if (number_bits == MaxGIFBits) \
568 max_code=MaxGIFTable; \
569 else \
570 max_code=MaxCode(number_bits); \
571 } \
572}
573
cristy4c08aed2011-07-01 19:47:50 +0000574 Quantum
cristy3ed852e2009-09-05 21:47:34 +0000575 index;
576
cristybb503372010-05-27 20:51:26 +0000577 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000578 i;
579
cristy3ed852e2009-09-05 21:47:34 +0000580 short
581 *hash_code,
582 *hash_prefix,
583 waiting_code;
584
cristybb503372010-05-27 20:51:26 +0000585 size_t
cristy3ed852e2009-09-05 21:47:34 +0000586 bits,
587 clear_code,
588 datum,
589 end_of_information_code,
590 free_code,
cristyebc891a2011-04-24 23:04:16 +0000591 length,
cristy3ed852e2009-09-05 21:47:34 +0000592 max_code,
593 next_pixel,
594 number_bits,
cristyebc891a2011-04-24 23:04:16 +0000595 one,
cristy3ed852e2009-09-05 21:47:34 +0000596 pass;
597
cristyebc891a2011-04-24 23:04:16 +0000598 ssize_t
599 displacement,
600 offset,
601 k,
602 y;
603
604 unsigned char
605 *packet,
606 *hash_suffix;
607
cristy3ed852e2009-09-05 21:47:34 +0000608 /*
609 Allocate encoder tables.
610 */
611 assert(image != (Image *) NULL);
cristy5d89f432010-05-30 00:00:26 +0000612 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000613 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
614 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
615 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
616 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
617 sizeof(*hash_suffix));
618 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
619 (hash_prefix == (short *) NULL) ||
620 (hash_suffix == (unsigned char *) NULL))
621 {
622 if (packet != (unsigned char *) NULL)
623 packet=(unsigned char *) RelinquishMagickMemory(packet);
624 if (hash_code != (short *) NULL)
625 hash_code=(short *) RelinquishMagickMemory(hash_code);
626 if (hash_prefix != (short *) NULL)
627 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
628 if (hash_suffix != (unsigned char *) NULL)
629 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
630 return(MagickFalse);
631 }
632 /*
633 Initialize GIF encoder.
634 */
635 number_bits=data_size;
636 max_code=MaxCode(number_bits);
cristy5d89f432010-05-30 00:00:26 +0000637 clear_code=((short) one << (data_size-1));
cristy3ed852e2009-09-05 21:47:34 +0000638 end_of_information_code=clear_code+1;
639 free_code=clear_code+2;
640 length=0;
641 datum=0;
642 bits=0;
643 for (i=0; i < MaxHashTable; i++)
644 hash_code[i]=0;
645 GIFOutputCode(clear_code);
646 /*
647 Encode pixels.
648 */
649 offset=0;
650 pass=0;
651 waiting_code=0;
cristybb503372010-05-27 20:51:26 +0000652 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000653 {
cristy4c08aed2011-07-01 19:47:50 +0000654 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000655 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000656
cristybb503372010-05-27 20:51:26 +0000657 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000658 x;
659
cristyc82a27b2011-10-21 01:07:16 +0000660 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000661 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000662 break;
cristy3ed852e2009-09-05 21:47:34 +0000663 if (y == 0)
cristy4c08aed2011-07-01 19:47:50 +0000664 {
665 waiting_code=(short) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +0000666 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000667 }
cristy15893a42010-11-20 18:57:15 +0000668 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000669 {
670 /*
671 Probe hash table.
672 */
cristy4c08aed2011-07-01 19:47:50 +0000673 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
cristyed231572011-07-14 02:18:59 +0000674 p+=GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +0000675 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000676 if (k >= MaxHashTable)
677 k-=MaxHashTable;
678 next_pixel=MagickFalse;
679 displacement=1;
680 if (hash_code[k] > 0)
681 {
682 if ((hash_prefix[k] == waiting_code) &&
683 (hash_suffix[k] == (unsigned char) index))
684 {
685 waiting_code=hash_code[k];
686 continue;
687 }
688 if (k != 0)
689 displacement=MaxHashTable-k;
690 for ( ; ; )
691 {
692 k-=displacement;
693 if (k < 0)
694 k+=MaxHashTable;
695 if (hash_code[k] == 0)
696 break;
697 if ((hash_prefix[k] == waiting_code) &&
698 (hash_suffix[k] == (unsigned char) index))
699 {
700 waiting_code=hash_code[k];
701 next_pixel=MagickTrue;
702 break;
703 }
704 }
705 if (next_pixel == MagickTrue)
706 continue;
707 }
cristybb503372010-05-27 20:51:26 +0000708 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000709 if (free_code < MaxGIFTable)
710 {
711 hash_code[k]=(short) free_code++;
712 hash_prefix[k]=waiting_code;
713 hash_suffix[k]=(unsigned char) index;
714 }
715 else
716 {
717 /*
718 Fill the hash table with empty entries.
719 */
720 for (k=0; k < MaxHashTable; k++)
721 hash_code[k]=0;
722 /*
723 Reset compressor and issue a clear code.
724 */
725 free_code=clear_code+2;
726 GIFOutputCode(clear_code);
727 number_bits=data_size;
728 max_code=MaxCode(number_bits);
729 }
730 waiting_code=(short) index;
731 }
732 if (image_info->interlace == NoInterlace)
733 offset++;
734 else
735 switch (pass)
736 {
737 case 0:
738 default:
739 {
740 offset+=8;
cristybb503372010-05-27 20:51:26 +0000741 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000742 {
743 pass++;
744 offset=4;
745 }
746 break;
747 }
748 case 1:
749 {
750 offset+=8;
cristybb503372010-05-27 20:51:26 +0000751 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000752 {
753 pass++;
754 offset=2;
755 }
756 break;
757 }
758 case 2:
759 {
760 offset+=4;
cristybb503372010-05-27 20:51:26 +0000761 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000762 {
763 pass++;
764 offset=1;
765 }
766 break;
767 }
768 case 3:
769 {
770 offset+=2;
771 break;
772 }
773 }
774 }
775 /*
776 Flush out the buffered code.
777 */
cristybb503372010-05-27 20:51:26 +0000778 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000779 GIFOutputCode(end_of_information_code);
780 if (bits > 0)
781 {
782 /*
783 Add a character to current packet.
784 */
785 packet[length++]=(unsigned char) (datum & 0xff);
786 if (length >= 254)
787 {
788 (void) WriteBlobByte(image,(unsigned char) length);
789 (void) WriteBlob(image,length,packet);
790 length=0;
791 }
792 }
793 /*
794 Flush accumulated data.
795 */
796 if (length > 0)
797 {
798 (void) WriteBlobByte(image,(unsigned char) length);
799 (void) WriteBlob(image,length,packet);
800 }
801 /*
802 Free encoder memory.
803 */
804 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
805 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
806 hash_code=(short *) RelinquishMagickMemory(hash_code);
807 packet=(unsigned char *) RelinquishMagickMemory(packet);
808 return(MagickTrue);
809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
816% I s G I F %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% IsGIF() returns MagickTrue if the image format type, identified by the
823% magick string, is GIF.
824%
825% The format of the IsGIF method is:
826%
827% MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
828%
829% A description of each parameter follows:
830%
831% o magick: compare image format pattern against these bytes.
832%
833% o length: Specifies the length of the magick string.
834%
835*/
836static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
837{
838 if (length < 4)
839 return(MagickFalse);
840 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
841 return(MagickTrue);
842 return(MagickFalse);
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
850+ R e a d B l o b B l o c k %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856% ReadBlobBlock() reads data from the image file and returns it. The
857% amount of data is determined by first reading a count byte. The number
858% of bytes read is returned.
859%
860% The format of the ReadBlobBlock method is:
861%
862% size_t ReadBlobBlock(Image *image,unsigned char *data)
863%
864% A description of each parameter follows:
865%
866% o image: the image.
867%
868% o data: Specifies an area to place the information requested from
869% the file.
870%
871*/
872static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
873{
874 ssize_t
875 count;
876
877 unsigned char
878 block_count;
879
880 assert(image != (Image *) NULL);
881 assert(image->signature == MagickSignature);
882 assert(data != (unsigned char *) NULL);
883 count=ReadBlob(image,1,&block_count);
884 if (count != 1)
885 return(0);
886 return(ReadBlob(image,(size_t) block_count,data));
887}
888
889/*
890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891% %
892% %
893% %
894% R e a d G I F I m a g e %
895% %
896% %
897% %
898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899%
900% ReadGIFImage() reads a Compuserve Graphics image file and returns it.
901% It allocates the memory necessary for the new Image structure and returns a
902% pointer to the new image.
903%
904% The format of the ReadGIFImage method is:
905%
906% Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
907%
908% A description of each parameter follows:
909%
910% o image_info: the image info.
911%
912% o exception: return any errors or warnings in this structure.
913%
914*/
915
916static inline size_t MagickMax(const size_t x,const size_t y)
917{
918 if (x > y)
919 return(x);
920 return(y);
921}
922
923static inline size_t MagickMin(const size_t x,const size_t y)
924{
925 if (x < y)
926 return(x);
927 return(y);
928}
929
cristy018f07f2011-09-04 21:15:19 +0000930static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000931{
932 unsigned char
933 buffer[256],
934 length,
935 data_size;
936
937 assert(image != (Image *) NULL);
938 assert(image->signature == MagickSignature);
939 if (image->debug != MagickFalse)
940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
941 if (ReadBlob(image,1,&data_size) != 1)
942 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
943 if (data_size > MaximumLZWBits)
944 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
945 if (ReadBlob(image,1,&length) != 1)
946 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
947 while (length != 0)
948 {
949 if (ReadBlob(image,length,buffer) != (ssize_t) length)
950 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
951 if (ReadBlob(image,1,&length) != 1)
952 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
953 }
954 return(MagickTrue);
955}
956
957static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
958{
959#define BitSet(byte,bit) (((byte) & (bit)) == (bit))
960#define LSBFirstOrder(x,y) (((y) << 8) | (x))
961
962 Image
963 *image;
964
965 int
966 number_extensionss=0;
967
cristy3ed852e2009-09-05 21:47:34 +0000968 MagickBooleanType
969 status;
970
971 RectangleInfo
972 page;
973
cristybb503372010-05-27 20:51:26 +0000974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000975 i;
976
977 register unsigned char
978 *p;
979
cristyebc891a2011-04-24 23:04:16 +0000980 size_t
981 delay,
982 dispose,
983 global_colors,
984 image_count,
985 iterations,
986 one;
987
cristy3ed852e2009-09-05 21:47:34 +0000988 ssize_t
cristyebc891a2011-04-24 23:04:16 +0000989 count,
990 opacity;
cristy3ed852e2009-09-05 21:47:34 +0000991
992 unsigned char
993 background,
994 c,
995 flag,
996 *global_colormap,
997 header[MaxTextExtent],
998 magick[12];
999
cristy3ed852e2009-09-05 21:47:34 +00001000 /*
1001 Open image file.
1002 */
1003 assert(image_info != (const ImageInfo *) NULL);
1004 assert(image_info->signature == MagickSignature);
1005 if (image_info->debug != MagickFalse)
1006 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1007 image_info->filename);
1008 assert(exception != (ExceptionInfo *) NULL);
1009 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +00001010 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001011 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1012 if (status == MagickFalse)
1013 {
1014 image=DestroyImageList(image);
1015 return((Image *) NULL);
1016 }
1017 /*
1018 Determine if this a GIF file.
1019 */
1020 count=ReadBlob(image,6,magick);
1021 if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1022 (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1023 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1024 page.width=ReadBlobLSBShort(image);
1025 page.height=ReadBlobLSBShort(image);
1026 flag=(unsigned char) ReadBlobByte(image);
1027 background=(unsigned char) ReadBlobByte(image);
1028 c=(unsigned char) ReadBlobByte(image); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001029 one=1;
1030 global_colors=one << (((size_t) flag & 0x07)+1);
cristy3ed852e2009-09-05 21:47:34 +00001031 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1032 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1033 if (global_colormap == (unsigned char *) NULL)
1034 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1035 if (BitSet((int) flag,0x80) != 0)
1036 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1037 delay=0;
1038 dispose=0;
1039 iterations=1;
1040 opacity=(-1);
1041 image_count=0;
1042 for ( ; ; )
1043 {
1044 count=ReadBlob(image,1,&c);
1045 if (count != 1)
1046 break;
1047 if (c == (unsigned char) ';')
1048 break; /* terminator */
1049 if (c == (unsigned char) '!')
1050 {
1051 /*
1052 GIF Extension block.
1053 */
1054
1055 count=ReadBlob(image,1,&c);
1056 if (count != 1)
1057 {
1058 global_colormap=(unsigned char *) RelinquishMagickMemory(
1059 global_colormap);
1060 ThrowReaderException(CorruptImageError,
1061 "UnableToReadExtensionBlock");
1062 }
1063 switch (c)
1064 {
1065 case 0xf9:
1066 {
1067 /*
1068 Read graphics control extension.
1069 */
1070 while (ReadBlobBlock(image,header) != 0) ;
cristybb503372010-05-27 20:51:26 +00001071 dispose=(size_t) (header[0] >> 2);
1072 delay=(size_t) ((header[2] << 8) | header[1]);
1073 if ((ssize_t) (header[0] & 0x01) == 0x01)
1074 opacity=(ssize_t) header[3];
cristy3ed852e2009-09-05 21:47:34 +00001075 break;
1076 }
1077 case 0xfe:
1078 {
1079 char
1080 *comments;
1081
cristyea43ca32012-05-20 23:47:26 +00001082 size_t
1083 length;
1084
cristy3ed852e2009-09-05 21:47:34 +00001085 /*
1086 Read comment extension.
1087 */
1088 comments=AcquireString((char *) NULL);
cristyea43ca32012-05-20 23:47:26 +00001089 for (length=0; ; length+=count)
cristy3ed852e2009-09-05 21:47:34 +00001090 {
1091 count=(ssize_t) ReadBlobBlock(image,header);
1092 if (count == 0)
1093 break;
1094 header[count]='\0';
cristyea43ca32012-05-20 23:47:26 +00001095 comments=(char *) ResizeQuantumMemory(comments,length+count,
1096 sizeof(*comments));
1097 if (comments == (char *) NULL)
1098 ThrowReaderException(ResourceLimitError,
1099 "MemoryAllocationFailed");
1100 (void) CopyMagickMemory(comments+length,header,(size_t) count);
cristy3ed852e2009-09-05 21:47:34 +00001101 }
cristyfa31e132012-05-31 14:12:41 +00001102 comments[length+count]='\0';
cristyd15e6592011-10-15 00:13:06 +00001103 (void) SetImageProperty(image,"comment",comments,exception);
cristy3ed852e2009-09-05 21:47:34 +00001104 comments=DestroyString(comments);
1105 break;
1106 }
1107 case 0xff:
1108 {
cristy3ed852e2009-09-05 21:47:34 +00001109 MagickBooleanType
1110 loop;
1111
1112 /*
1113 Read Netscape Loop extension.
1114 */
1115 loop=MagickFalse;
1116 if (ReadBlobBlock(image,header) != 0)
1117 loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1118 MagickTrue : MagickFalse;
1119 if (loop != MagickFalse)
1120 {
1121 while (ReadBlobBlock(image,header) != 0)
cristybb503372010-05-27 20:51:26 +00001122 iterations=(size_t) ((header[2] << 8) | header[1]);
cristy3ed852e2009-09-05 21:47:34 +00001123 break;
1124 }
1125 else
1126 {
1127 char
1128 name[MaxTextExtent];
1129
1130 int
1131 block_length,
1132 info_length,
1133 reserved_length;
1134
1135 MagickBooleanType
1136 i8bim,
1137 icc,
1138 iptc;
1139
1140 StringInfo
1141 *profile;
1142
1143 unsigned char
1144 *info;
1145
1146 /*
1147 Store GIF application extension as a generic profile.
1148 */
1149 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1150 MagickTrue : MagickFalse;
1151 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1152 MagickTrue : MagickFalse;
1153 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1154 MagickTrue : MagickFalse;
1155 number_extensionss++;
1156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1157 " Reading GIF application extension");
1158 info=(unsigned char *) AcquireQuantumMemory(255UL,
1159 sizeof(*info));
cristybf6abbe2011-09-01 13:20:47 +00001160 if (info == (unsigned char *) NULL)
1161 ThrowReaderException(ResourceLimitError,
1162 "MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001163 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));
cristybf6abbe2011-09-01 13:20:47 +00001175 if (info == (unsigned char *) NULL)
1176 ThrowReaderException(ResourceLimitError,
1177 "MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001178 }
1179 }
cristybf6abbe2011-09-01 13:20:47 +00001180 profile=BlobToStringInfo(info,(size_t) info_length);
1181 if (profile == (StringInfo *) NULL)
1182 ThrowReaderException(ResourceLimitError,
1183 "MemoryAllocationFailed");
1184 info=(unsigned char *) RelinquishMagickMemory(info);
cristy3ed852e2009-09-05 21:47:34 +00001185 if (i8bim == MagickTrue)
1186 (void) CopyMagickString(name,"8bim",sizeof(name));
1187 else if (icc == MagickTrue)
1188 (void) CopyMagickString(name,"icc",sizeof(name));
1189 else if (iptc == MagickTrue)
1190 (void) CopyMagickString(name,"iptc",sizeof(name));
1191 else
cristyb51dff52011-05-19 16:55:47 +00001192 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
cristy3ed852e2009-09-05 21:47:34 +00001193 header);
cristyd15e6592011-10-15 00:13:06 +00001194 (void) SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001195 profile=DestroyStringInfo(profile);
1196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1197 " profile name=%s",name);
1198 }
1199 break;
1200 }
1201 default:
1202 {
1203 while (ReadBlobBlock(image,header) != 0) ;
1204 break;
1205 }
1206 }
1207 }
1208 if (c != (unsigned char) ',')
1209 continue;
1210 if (image_count != 0)
1211 {
1212 /*
1213 Allocate next image structure.
1214 */
cristy9950d572011-10-01 18:22:35 +00001215 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001216 if (GetNextImageInList(image) == (Image *) NULL)
1217 {
1218 image=DestroyImageList(image);
1219 global_colormap=(unsigned char *) RelinquishMagickMemory(
1220 global_colormap);
1221 return((Image *) NULL);
1222 }
1223 image=SyncNextImageInList(image);
1224 }
1225 image_count++;
1226 /*
1227 Read image attributes.
1228 */
1229 image->storage_class=PseudoClass;
1230 image->compression=LZWCompression;
cristybb503372010-05-27 20:51:26 +00001231 page.x=(ssize_t) ReadBlobLSBShort(image);
1232 page.y=(ssize_t) ReadBlobLSBShort(image);
cristy3ed852e2009-09-05 21:47:34 +00001233 image->columns=ReadBlobLSBShort(image);
1234 image->rows=ReadBlobLSBShort(image);
1235 image->depth=8;
1236 flag=(unsigned char) ReadBlobByte(image);
cristy96c4fcb2012-02-08 01:01:05 +00001237 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
1238 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1239 ((size_t) (flag & 0x07)+1);
cristybb503372010-05-27 20:51:26 +00001240 if (opacity >= (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001241 opacity=(-1);
1242 image->page.width=page.width;
1243 image->page.height=page.height;
1244 image->page.y=page.y;
1245 image->page.x=page.x;
1246 image->delay=delay;
1247 image->ticks_per_second=100;
1248 image->dispose=(DisposeType) dispose;
1249 image->iterations=iterations;
1250 image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1251 delay=0;
1252 dispose=0;
1253 iterations=1;
1254 if ((image->columns == 0) || (image->rows == 0))
1255 {
1256 global_colormap=(unsigned char *) RelinquishMagickMemory(
1257 global_colormap);
1258 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1259 }
1260 /*
1261 Inititialize colormap.
1262 */
cristy018f07f2011-09-04 21:15:19 +00001263 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001264 {
1265 global_colormap=(unsigned char *) RelinquishMagickMemory(
1266 global_colormap);
1267 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1268 }
1269 if (BitSet((int) flag,0x80) == 0)
1270 {
1271 /*
1272 Use global colormap.
1273 */
1274 p=global_colormap;
cristybb503372010-05-27 20:51:26 +00001275 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001276 {
cristyea43ca32012-05-20 23:47:26 +00001277 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1278 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1279 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001280 if (i == opacity)
1281 {
cristyea43ca32012-05-20 23:47:26 +00001282 image->colormap[i].alpha=(double) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001283 image->transparent_color=image->colormap[opacity];
1284 }
1285 }
1286 image->background_color=image->colormap[MagickMin(background,
1287 image->colors-1)];
1288 }
1289 else
1290 {
1291 unsigned char
1292 *colormap;
1293
1294 /*
1295 Read local colormap.
1296 */
cristy96c4fcb2012-02-08 01:01:05 +00001297 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,3*
1298 sizeof(*colormap));
cristy3ed852e2009-09-05 21:47:34 +00001299 if (colormap == (unsigned char *) NULL)
1300 {
1301 global_colormap=(unsigned char *) RelinquishMagickMemory(
1302 global_colormap);
1303 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1304 }
1305 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1306 if (count != (ssize_t) (3*image->colors))
1307 {
1308 global_colormap=(unsigned char *) RelinquishMagickMemory(
1309 global_colormap);
1310 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1311 ThrowReaderException(CorruptImageError,
1312 "InsufficientImageDataInFile");
1313 }
1314 p=colormap;
cristybb503372010-05-27 20:51:26 +00001315 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001316 {
cristyea43ca32012-05-20 23:47:26 +00001317 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1318 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1319 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001320 if (i == opacity)
cristyea43ca32012-05-20 23:47:26 +00001321 image->colormap[i].alpha=(double) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001322 }
1323 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1324 }
cristy0a215142012-06-01 16:42:19 +00001325 for (i=0; i < (ssize_t) image->colors; i++)
1326 if ((image->colormap[i].red != image->colormap[i].green) ||
1327 (image->colormap[i].green != image->colormap[i].blue))
1328 break;
1329 if (i == (ssize_t) image->colors)
1330 SetImageColorspace(image,GRAYColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001331 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1332 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1333 break;
1334 /*
1335 Decode image.
1336 */
1337 if (image_info->ping != MagickFalse)
cristy018f07f2011-09-04 21:15:19 +00001338 status=PingGIFImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001339 else
cristy018f07f2011-09-04 21:15:19 +00001340 status=DecodeImage(image,opacity,exception);
cristy3ed852e2009-09-05 21:47:34 +00001341 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1342 {
1343 global_colormap=(unsigned char *) RelinquishMagickMemory(
1344 global_colormap);
1345 ThrowReaderException(CorruptImageError,"CorruptImage");
1346 }
1347 if (image_info->number_scenes != 0)
1348 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1349 break;
1350 opacity=(-1);
1351 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1352 1,image->scene);
1353 if (status == MagickFalse)
1354 break;
1355 }
1356 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1357 if ((image->columns == 0) || (image->rows == 0))
1358 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1359 (void) CloseBlob(image);
1360 return(GetFirstImageInList(image));
1361}
1362
1363/*
1364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1365% %
1366% %
1367% %
1368% R e g i s t e r G I F I m a g e %
1369% %
1370% %
1371% %
1372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1373%
1374% RegisterGIFImage() adds properties for the GIF image format to
1375% the list of supported formats. The properties include the image format
1376% tag, a method to read and/or write the format, whether the format
1377% supports the saving of more than one frame to the same file or blob,
1378% whether the format supports native in-memory I/O, and a brief
1379% description of the format.
1380%
1381% The format of the RegisterGIFImage method is:
1382%
cristybb503372010-05-27 20:51:26 +00001383% size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001384%
1385*/
cristybb503372010-05-27 20:51:26 +00001386ModuleExport size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001387{
1388 MagickInfo
1389 *entry;
1390
1391 entry=SetMagickInfo("GIF");
1392 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1393 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1394 entry->magick=(IsImageFormatHandler *) IsGIF;
1395 entry->description=ConstantString("CompuServe graphics interchange format");
1396 entry->module=ConstantString("GIF");
1397 (void) RegisterMagickInfo(entry);
1398 entry=SetMagickInfo("GIF87");
1399 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1400 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1401 entry->magick=(IsImageFormatHandler *) IsGIF;
1402 entry->adjoin=MagickFalse;
1403 entry->description=ConstantString("CompuServe graphics interchange format");
1404 entry->version=ConstantString("version 87a");
1405 entry->module=ConstantString("GIF");
1406 (void) RegisterMagickInfo(entry);
1407 return(MagickImageCoderSignature);
1408}
1409
1410/*
1411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412% %
1413% %
1414% %
1415% U n r e g i s t e r G I F I m a g e %
1416% %
1417% %
1418% %
1419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420%
1421% UnregisterGIFImage() removes format registrations made by the
1422% GIF module from the list of supported formats.
1423%
1424% The format of the UnregisterGIFImage method is:
1425%
1426% UnregisterGIFImage(void)
1427%
1428*/
1429ModuleExport void UnregisterGIFImage(void)
1430{
1431 (void) UnregisterMagickInfo("GIF");
1432 (void) UnregisterMagickInfo("GIF87");
1433}
1434
1435/*
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437% %
1438% %
1439% %
1440% W r i t e G I F I m a g e %
1441% %
1442% %
1443% %
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%
1446% WriteGIFImage() writes an image to a file in the Compuserve Graphics
1447% image format.
1448%
1449% The format of the WriteGIFImage method is:
1450%
cristy1e178e72011-08-28 19:44:34 +00001451% MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1452% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001453%
1454% A description of each parameter follows.
1455%
1456% o image_info: the image info.
1457%
1458% o image: The image.
1459%
cristy1e178e72011-08-28 19:44:34 +00001460% o exception: return any errors or warnings in this structure.
1461%
cristy3ed852e2009-09-05 21:47:34 +00001462*/
cristy1e178e72011-08-28 19:44:34 +00001463static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1464 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001465{
cristy3ed852e2009-09-05 21:47:34 +00001466 int
1467 c;
1468
cristy3ed852e2009-09-05 21:47:34 +00001469 ImageInfo
1470 *write_info;
1471
1472 InterlaceType
1473 interlace;
1474
1475 MagickBooleanType
1476 status;
1477
1478 MagickOffsetType
1479 scene;
1480
1481 RectangleInfo
1482 page;
1483
cristybb503372010-05-27 20:51:26 +00001484 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001485 i;
1486
1487 register unsigned char
1488 *q;
1489
1490 size_t
cristyebc891a2011-04-24 23:04:16 +00001491 bits_per_pixel,
1492 delay,
1493 length,
1494 one;
1495
1496 ssize_t
1497 j,
1498 opacity;
cristy3ed852e2009-09-05 21:47:34 +00001499
1500 unsigned char
1501 *colormap,
1502 *global_colormap;
1503
cristy3ed852e2009-09-05 21:47:34 +00001504 /*
1505 Open output image file.
1506 */
1507 assert(image_info != (const ImageInfo *) NULL);
1508 assert(image_info->signature == MagickSignature);
1509 assert(image != (Image *) NULL);
1510 assert(image->signature == MagickSignature);
1511 if (image->debug != MagickFalse)
1512 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001513 assert(exception != (ExceptionInfo *) NULL);
1514 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001515 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001516 if (status == MagickFalse)
1517 return(status);
1518 /*
1519 Allocate colormap.
1520 */
1521 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1522 sizeof(*global_colormap));
1523 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1524 if ((global_colormap == (unsigned char *) NULL) ||
1525 (colormap == (unsigned char *) NULL))
1526 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1527 for (i=0; i < 768; i++)
1528 colormap[i]=(unsigned char) 0;
1529 /*
1530 Write GIF header.
1531 */
1532 write_info=CloneImageInfo(image_info);
1533 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1534 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1535 else
1536 {
1537 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1538 write_info->adjoin=MagickFalse;
1539 }
1540 /*
1541 Determine image bounding box.
1542 */
1543 page.width=image->columns;
glennrpff6c41f2011-10-31 15:57:41 +00001544 if (image->page.width > page.width)
1545 page.width=image->page.width;
cristy3ed852e2009-09-05 21:47:34 +00001546 page.height=image->rows;
glennrpff6c41f2011-10-31 15:57:41 +00001547 if (image->page.height > page.height)
1548 page.height=image->page.height;
1549 page.x=image->page.x;
1550 page.y=image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001551 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1552 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1553 /*
1554 Write images to file.
1555 */
1556 interlace=write_info->interlace;
1557 if ((write_info->adjoin != MagickFalse) &&
1558 (GetNextImageInList(image) != (Image *) NULL))
1559 interlace=NoInterlace;
1560 scene=0;
cristy5d89f432010-05-30 00:00:26 +00001561 one=1;
cristy3ed852e2009-09-05 21:47:34 +00001562 do
1563 {
cristy501c5592012-04-18 12:45:09 +00001564 if (IssRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00001565 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001566 opacity=(-1);
cristy1e178e72011-08-28 19:44:34 +00001567 if (IsImageOpaque(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001568 {
1569 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +00001570 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +00001571 }
1572 else
1573 {
1574 MagickRealType
1575 alpha,
1576 beta;
1577
1578 /*
1579 Identify transparent colormap index.
1580 */
1581 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +00001582 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +00001583 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001584 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00001585 {
1586 if (opacity < 0)
1587 {
1588 opacity=i;
1589 continue;
1590 }
cristy4c08aed2011-07-01 19:47:50 +00001591 alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
1592 image->colormap[i].alpha;
1593 beta=(MagickRealType) TransparentAlpha-(MagickRealType)
1594 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +00001595 if (alpha < beta)
1596 opacity=i;
1597 }
1598 if (opacity == -1)
1599 {
cristy018f07f2011-09-04 21:15:19 +00001600 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +00001601 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001602 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00001603 {
1604 if (opacity < 0)
1605 {
1606 opacity=i;
1607 continue;
1608 }
cristy4c08aed2011-07-01 19:47:50 +00001609 alpha=(Quantum) TransparentAlpha-(MagickRealType)
1610 image->colormap[i].alpha;
1611 beta=(Quantum) TransparentAlpha-(MagickRealType)
1612 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +00001613 if (alpha < beta)
1614 opacity=i;
1615 }
1616 }
1617 if (opacity >= 0)
1618 {
1619 image->colormap[opacity].red=image->transparent_color.red;
1620 image->colormap[opacity].green=image->transparent_color.green;
1621 image->colormap[opacity].blue=image->transparent_color.blue;
1622 }
1623 }
1624 if ((image->storage_class == DirectClass) || (image->colors > 256))
1625 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1626 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
cristy5d89f432010-05-30 00:00:26 +00001627 if ((one << bits_per_pixel) >= image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001628 break;
1629 q=colormap;
cristybb503372010-05-27 20:51:26 +00001630 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001631 {
cristyea43ca32012-05-20 23:47:26 +00001632 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
1633 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
1634 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
cristy3ed852e2009-09-05 21:47:34 +00001635 }
cristy5d89f432010-05-30 00:00:26 +00001636 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00001637 {
1638 *q++=(unsigned char) 0x0;
1639 *q++=(unsigned char) 0x0;
1640 *q++=(unsigned char) 0x0;
1641 }
1642 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1643 (write_info->adjoin == MagickFalse))
1644 {
1645 /*
1646 Write global colormap.
1647 */
1648 c=0x80;
1649 c|=(8-1) << 4; /* color resolution */
1650 c|=(bits_per_pixel-1); /* size of global colormap */
1651 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001652 for (j=0; j < (ssize_t) image->colors; j++)
cristy101ab702011-10-13 13:06:32 +00001653 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
cristy3ed852e2009-09-05 21:47:34 +00001654 break;
1655 (void) WriteBlobByte(image,(unsigned char)
cristybb503372010-05-27 20:51:26 +00001656 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
cristy3ed852e2009-09-05 21:47:34 +00001657 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001658 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001659 (void) WriteBlob(image,length,colormap);
1660 for (j=0; j < 768; j++)
1661 global_colormap[j]=colormap[j];
1662 }
1663 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1664 {
1665 /*
1666 Write graphics control extension.
1667 */
1668 (void) WriteBlobByte(image,(unsigned char) 0x21);
1669 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1670 (void) WriteBlobByte(image,(unsigned char) 0x04);
1671 c=image->dispose << 2;
1672 if (opacity >= 0)
1673 c|=0x01;
1674 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001675 delay=(size_t) (100*image->delay/MagickMax((size_t)
cristy3ed852e2009-09-05 21:47:34 +00001676 image->ticks_per_second,1));
1677 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1678 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1679 0));
1680 (void) WriteBlobByte(image,(unsigned char) 0x00);
1681 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
cristyd15e6592011-10-15 00:13:06 +00001682 (GetImageProperty(image,"comment",exception) != (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001683 {
cristyfa31e132012-05-31 14:12:41 +00001684 const char
1685 *value;
1686
1687 register const char
1688 *p;
cristy3ed852e2009-09-05 21:47:34 +00001689
1690 size_t
1691 count;
cristyfa31e132012-05-31 14:12:41 +00001692
cristy3ed852e2009-09-05 21:47:34 +00001693 /*
cristyfa31e132012-05-31 14:12:41 +00001694 Write Comment extension.
1695 */
1696 (void) WriteBlobByte(image,(unsigned char) 0x21);
1697 (void) WriteBlobByte(image,(unsigned char) 0xfe);
cristy02eb33d2012-05-31 14:48:55 +00001698 value=GetImageProperty(image,"comment",exception);
cristyfa31e132012-05-31 14:12:41 +00001699 for (p=value; *p != '\0'; )
cristy3ed852e2009-09-05 21:47:34 +00001700 {
cristyfa31e132012-05-31 14:12:41 +00001701 count=MagickMin(strlen(p),255);
1702 (void) WriteBlobByte(image,(unsigned char) count);
1703 for (i=0; i < (ssize_t) count; i++)
1704 (void) WriteBlobByte(image,(unsigned char) *p++);
cristy3ed852e2009-09-05 21:47:34 +00001705 }
cristyfa31e132012-05-31 14:12:41 +00001706 (void) WriteBlobByte(image,(unsigned char) 0x00);
cristy3ed852e2009-09-05 21:47:34 +00001707 }
1708 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1709 (GetNextImageInList(image) != (Image *) NULL) &&
1710 (image->iterations != 1))
1711 {
1712 /*
1713 Write Netscape Loop extension.
1714 */
1715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1716 " Writing GIF Extension %s","NETSCAPE2.0");
1717 (void) WriteBlobByte(image,(unsigned char) 0x21);
1718 (void) WriteBlobByte(image,(unsigned char) 0xff);
1719 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1720 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1721 (void) WriteBlobByte(image,(unsigned char) 0x03);
1722 (void) WriteBlobByte(image,(unsigned char) 0x01);
1723 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1724 (void) WriteBlobByte(image,(unsigned char) 0x00);
1725 }
1726 ResetImageProfileIterator(image);
1727 for ( ; ; )
1728 {
1729 char
1730 *name;
1731
1732 const StringInfo
1733 *profile;
1734
1735 name=GetNextImageProfile(image);
1736 if (name == (const char *) NULL)
1737 break;
1738 profile=GetImageProfile(image,name);
1739 if (profile != (StringInfo *) NULL)
1740 {
1741 if ((LocaleCompare(name,"ICC") == 0) ||
1742 (LocaleCompare(name,"ICM") == 0) ||
1743 (LocaleCompare(name,"IPTC") == 0) ||
1744 (LocaleCompare(name,"8BIM") == 0) ||
1745 (LocaleNCompare(name,"gif:",4) == 0))
1746 {
1747 size_t
1748 length;
1749
1750 ssize_t
1751 offset;
1752
1753 unsigned char
1754 *datum;
1755
1756 datum=GetStringInfoDatum(profile);
1757 length=GetStringInfoLength(profile);
1758 (void) WriteBlobByte(image,(unsigned char) 0x21);
1759 (void) WriteBlobByte(image,(unsigned char) 0xff);
1760 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1761 if ((LocaleCompare(name,"ICC") == 0) ||
1762 (LocaleCompare(name,"ICM") == 0))
1763 {
1764 /*
1765 Write ICC extension.
1766 */
1767 (void) WriteBlob(image,11,(unsigned char *)"ICCRGBG1012");
1768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1769 " Writing GIF Extension %s","ICCRGBG1012");
1770 }
1771 else
1772 if ((LocaleCompare(name,"IPTC") == 0))
1773 {
1774 /*
1775 write IPTC extension.
1776 */
1777 (void) WriteBlob(image,11,(unsigned char *)"MGKIPTC0000");
1778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1779 " Writing GIF Extension %s","MGKIPTC0000");
1780 }
1781 else
1782 if ((LocaleCompare(name,"8BIM") == 0))
1783 {
1784 /*
1785 Write 8BIM extension>
1786 */
1787 (void) WriteBlob(image,11,(unsigned char *)
1788 "MGK8BIM0000");
1789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1790 " Writing GIF Extension %s","MGK8BIM0000");
1791 }
1792 else
1793 {
1794 char
1795 extension[MaxTextExtent];
1796
1797 /* write generic extension */
1798 (void) CopyMagickString(extension,name+4,
1799 sizeof(extension));
1800 (void) WriteBlob(image,11,(unsigned char *) extension);
1801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1802 " Writing GIF Extension %s",name);
1803 }
1804 offset=0;
1805 while ((ssize_t) length > offset)
1806 {
1807 size_t
1808 block_length;
1809
1810 if ((length-offset) < 255)
1811 block_length=length-offset;
1812 else
1813 block_length=255;
1814 (void) WriteBlobByte(image,(unsigned char) block_length);
1815 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1816 offset+=(ssize_t) block_length;
1817 }
1818 (void) WriteBlobByte(image,(unsigned char) 0x00);
1819 }
1820 }
1821 }
1822 }
1823 (void) WriteBlobByte(image,','); /* image separator */
1824 /*
1825 Write the image header.
1826 */
1827 page.x=image->page.x;
1828 page.y=image->page.y;
1829 if ((image->page.width != 0) && (image->page.height != 0))
1830 page=image->page;
1831 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1832 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1833 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1834 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1835 c=0x00;
1836 if (interlace != NoInterlace)
1837 c|=0x40; /* pixel data is interlaced */
cristybb503372010-05-27 20:51:26 +00001838 for (j=0; j < (ssize_t) (3*image->colors); j++)
cristy3ed852e2009-09-05 21:47:34 +00001839 if (colormap[j] != global_colormap[j])
1840 break;
cristybb503372010-05-27 20:51:26 +00001841 if (j == (ssize_t) (3*image->colors))
cristy3ed852e2009-09-05 21:47:34 +00001842 (void) WriteBlobByte(image,(unsigned char) c);
1843 else
1844 {
1845 c|=0x80;
1846 c|=(bits_per_pixel-1); /* size of local colormap */
1847 (void) WriteBlobByte(image,(unsigned char) c);
cristy5d89f432010-05-30 00:00:26 +00001848 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001849 (void) WriteBlob(image,length,colormap);
1850 }
1851 /*
1852 Write the image data.
1853 */
1854 c=(int) MagickMax(bits_per_pixel,2);
1855 (void) WriteBlobByte(image,(unsigned char) c);
cristyc82a27b2011-10-21 01:07:16 +00001856 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1857 exception);
cristy3ed852e2009-09-05 21:47:34 +00001858 if (status == MagickFalse)
1859 {
1860 global_colormap=(unsigned char *) RelinquishMagickMemory(
1861 global_colormap);
1862 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1863 write_info=DestroyImageInfo(write_info);
1864 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1865 }
1866 (void) WriteBlobByte(image,(unsigned char) 0x00);
1867 if (GetNextImageInList(image) == (Image *) NULL)
1868 break;
1869 image=SyncNextImageInList(image);
1870 scene++;
1871 status=SetImageProgress(image,SaveImagesTag,scene,
1872 GetImageListLength(image));
1873 if (status == MagickFalse)
1874 break;
1875 } while (write_info->adjoin != MagickFalse);
1876 (void) WriteBlobByte(image,';'); /* terminator */
1877 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1878 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1879 write_info=DestroyImageInfo(write_info);
1880 (void) CloseBlob(image);
1881 return(MagickTrue);
1882}