blob: 94a93e30d9e71f86aa6f184210d2d22f852c2230 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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"
cristy03a398b2012-06-22 12:06:04 +000071#include "MagickCore/string-private.h"
cristy4c08aed2011-07-01 19:47:50 +000072#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000073
74/*
75 Define declarations.
76*/
77#define MaximumLZWBits 12
78#define MaximumLZWCode (1UL << MaximumLZWBits)
79
80/*
81 Typdef declarations.
82*/
83typedef struct _LZWCodeInfo
84{
85 unsigned char
86 buffer[280];
87
cristybb503372010-05-27 20:51:26 +000088 size_t
cristy3ed852e2009-09-05 21:47:34 +000089 count,
90 bit;
91
92 MagickBooleanType
93 eof;
94} LZWCodeInfo;
95
96typedef struct _LZWStack
97{
cristybb503372010-05-27 20:51:26 +000098 size_t
cristy3ed852e2009-09-05 21:47:34 +000099 *codes,
100 *index,
101 *top;
102} LZWStack;
103
104typedef struct _LZWInfo
105{
106 Image
107 *image;
108
109 LZWStack
110 *stack;
111
112 MagickBooleanType
113 genesis;
114
cristybb503372010-05-27 20:51:26 +0000115 size_t
cristy3ed852e2009-09-05 21:47:34 +0000116 data_size,
117 maximum_data_value,
118 clear_code,
119 end_code,
120 bits,
121 first_code,
122 last_code,
123 maximum_code,
124 slot,
125 *table[2];
126
127 LZWCodeInfo
128 code_info;
129} LZWInfo;
130
131/*
132 Forward declarations.
133*/
134static inline int
cristybb503372010-05-27 20:51:26 +0000135 GetNextLZWCode(LZWInfo *,const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000136
137static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000138 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000139
140static ssize_t
141 ReadBlobBlock(Image *,unsigned char *);
142
143/*
144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145% %
146% %
147% %
148% D e c o d e I m a g e %
149% %
150% %
151% %
152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153%
154% DecodeImage uncompresses an image via GIF-coding.
155%
156% The format of the DecodeImage method is:
157%
cristybb503372010-05-27 20:51:26 +0000158% MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
cristy3ed852e2009-09-05 21:47:34 +0000159%
160% A description of each parameter follows:
161%
162% o image: the address of a structure of type Image.
163%
164% o opacity: The colormap index associated with the transparent color.
165%
166*/
167
168static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
169{
cristybb503372010-05-27 20:51:26 +0000170 if (lzw_info->table[0] != (size_t *) NULL)
171 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000172 lzw_info->table[0]);
cristybb503372010-05-27 20:51:26 +0000173 if (lzw_info->table[1] != (size_t *) NULL)
174 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000175 lzw_info->table[1]);
176 if (lzw_info->stack != (LZWStack *) NULL)
177 {
cristybb503372010-05-27 20:51:26 +0000178 if (lzw_info->stack->codes != (size_t *) NULL)
179 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
cristy3ed852e2009-09-05 21:47:34 +0000180 lzw_info->stack->codes);
181 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
182 }
183 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
184 return((LZWInfo *) NULL);
185}
186
187static inline void ResetLZWInfo(LZWInfo *lzw_info)
188{
cristyeaedf062010-05-29 22:36:02 +0000189 size_t
190 one;
191
cristy3ed852e2009-09-05 21:47:34 +0000192 lzw_info->bits=lzw_info->data_size+1;
cristy5d89f432010-05-30 00:00:26 +0000193 one=1;
cristyeaedf062010-05-29 22:36:02 +0000194 lzw_info->maximum_code=one << lzw_info->bits;
cristy3ed852e2009-09-05 21:47:34 +0000195 lzw_info->slot=lzw_info->maximum_data_value+3;
196 lzw_info->genesis=MagickTrue;
197}
198
cristybb503372010-05-27 20:51:26 +0000199static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000200{
201 LZWInfo
202 *lzw_info;
203
cristybb503372010-05-27 20:51:26 +0000204 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000205 i;
206
cristy5d89f432010-05-30 00:00:26 +0000207 size_t
208 one;
209
cristy73bd4a52010-10-05 11:24:23 +0000210 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
cristy3ed852e2009-09-05 21:47:34 +0000211 if (lzw_info == (LZWInfo *) NULL)
212 return((LZWInfo *) NULL);
213 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
214 lzw_info->image=image;
215 lzw_info->data_size=data_size;
cristy5d89f432010-05-30 00:00:26 +0000216 one=1;
217 lzw_info->maximum_data_value=(one << data_size)-1;
cristy3ed852e2009-09-05 21:47:34 +0000218 lzw_info->clear_code=lzw_info->maximum_data_value+1;
219 lzw_info->end_code=lzw_info->maximum_data_value+2;
cristybb503372010-05-27 20:51:26 +0000220 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
glennrp73921bd2015-01-31 00:03:01 +0000221 sizeof(**lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000222 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
glennrp73921bd2015-01-31 00:03:01 +0000223 sizeof(**lzw_info->table));
cristybb503372010-05-27 20:51:26 +0000224 if ((lzw_info->table[0] == (size_t *) NULL) ||
225 (lzw_info->table[1] == (size_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000226 {
227 lzw_info=RelinquishLZWInfo(lzw_info);
228 return((LZWInfo *) NULL);
229 }
cristybb503372010-05-27 20:51:26 +0000230 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
cristy3ed852e2009-09-05 21:47:34 +0000231 {
232 lzw_info->table[0][i]=0;
cristybb503372010-05-27 20:51:26 +0000233 lzw_info->table[1][i]=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +0000234 }
235 ResetLZWInfo(lzw_info);
236 lzw_info->code_info.buffer[0]='\0';
237 lzw_info->code_info.buffer[1]='\0';
238 lzw_info->code_info.count=2;
239 lzw_info->code_info.bit=8*lzw_info->code_info.count;
240 lzw_info->code_info.eof=MagickFalse;
241 lzw_info->genesis=MagickTrue;
cristy73bd4a52010-10-05 11:24:23 +0000242 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
cristy3ed852e2009-09-05 21:47:34 +0000243 if (lzw_info->stack == (LZWStack *) NULL)
244 {
245 lzw_info=RelinquishLZWInfo(lzw_info);
246 return((LZWInfo *) NULL);
247 }
cristybb503372010-05-27 20:51:26 +0000248 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
cristy3ed852e2009-09-05 21:47:34 +0000249 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
cristybb503372010-05-27 20:51:26 +0000250 if (lzw_info->stack->codes == (size_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000251 {
252 lzw_info=RelinquishLZWInfo(lzw_info);
253 return((LZWInfo *) NULL);
254 }
255 lzw_info->stack->index=lzw_info->stack->codes;
256 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
257 return(lzw_info);
258}
259
cristybb503372010-05-27 20:51:26 +0000260static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
cristy3ed852e2009-09-05 21:47:34 +0000261{
262 int
263 code;
264
cristybb503372010-05-27 20:51:26 +0000265 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000266 i;
267
cristy5d89f432010-05-30 00:00:26 +0000268 size_t
269 one;
270
cristy3ed852e2009-09-05 21:47:34 +0000271 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
272 (lzw_info->code_info.eof == MagickFalse))
273 {
274 ssize_t
275 count;
276
277 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
278 lzw_info->code_info.count-2];
279 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
280 lzw_info->code_info.count-1];
281 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
282 lzw_info->code_info.count=2;
283 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
284 lzw_info->code_info.count]);
285 if (count > 0)
286 lzw_info->code_info.count+=count;
287 else
288 lzw_info->code_info.eof=MagickTrue;
289 }
290 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
291 return(-1);
292 code=0;
cristy5d89f432010-05-30 00:00:26 +0000293 one=1;
cristybb503372010-05-27 20:51:26 +0000294 for (i=0; i < (ssize_t) bits; i++)
cristy3ed852e2009-09-05 21:47:34 +0000295 {
296 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
cristy5d89f432010-05-30 00:00:26 +0000297 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
cristy3ed852e2009-09-05 21:47:34 +0000298 lzw_info->code_info.bit++;
299 }
300 return(code);
301}
302
cristy15893a42010-11-20 18:57:15 +0000303static inline int PopLZWStack(LZWStack *stack_info)
cristy3ed852e2009-09-05 21:47:34 +0000304{
305 if (stack_info->index <= stack_info->codes)
306 return(-1);
307 stack_info->index--;
cristy15893a42010-11-20 18:57:15 +0000308 return((int) *stack_info->index);
cristy3ed852e2009-09-05 21:47:34 +0000309}
310
cristybb503372010-05-27 20:51:26 +0000311static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000312{
313 if (stack_info->index >= stack_info->top)
314 return;
315 *stack_info->index=value;
316 stack_info->index++;
317}
318
319static int ReadBlobLZWByte(LZWInfo *lzw_info)
320{
321 int
322 code;
323
cristybb503372010-05-27 20:51:26 +0000324 size_t
cristy5d89f432010-05-30 00:00:26 +0000325 one,
cristy3ed852e2009-09-05 21:47:34 +0000326 value;
327
cristy891bc7f2013-08-01 00:25:02 +0000328 ssize_t
329 count;
330
cristy3ed852e2009-09-05 21:47:34 +0000331 if (lzw_info->stack->index != lzw_info->stack->codes)
332 return(PopLZWStack(lzw_info->stack));
333 if (lzw_info->genesis != MagickFalse)
334 {
335 lzw_info->genesis=MagickFalse;
336 do
337 {
cristy891bc7f2013-08-01 00:25:02 +0000338 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,lzw_info->bits);
cristy3ed852e2009-09-05 21:47:34 +0000339 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
cristy4c08aed2011-07-01 19:47:50 +0000399 size_t
400 pass;
401
cristybb503372010-05-27 20:51:26 +0000402 ssize_t
cristycc3a7e72015-01-31 18:13:18 +0000403 index,
cristy3ed852e2009-09-05 21:47:34 +0000404 offset,
405 y;
406
cristy3ed852e2009-09-05 21:47:34 +0000407 unsigned char
408 data_size;
409
cristy3ed852e2009-09-05 21:47:34 +0000410 /*
411 Allocate decoder tables.
412 */
413 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000414 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000415 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);
cristy3ed852e2009-09-05 21:47:34 +0000424 pass=0;
425 offset=0;
cristybb503372010-05-27 20:51:26 +0000426 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000427 {
cristybb503372010-05-27 20:51:26 +0000428 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000429 x;
430
cristy4c08aed2011-07-01 19:47:50 +0000431 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100432 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000433
cristy31752352013-01-26 01:10:47 +0000434 q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000435 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000436 break;
cristybb503372010-05-27 20:51:26 +0000437 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +0000438 {
439 c=ReadBlobLZWByte(lzw_info);
440 if (c < 0)
441 break;
cristycc3a7e72015-01-31 18:13:18 +0000442 index=ConstrainColormapIndex(image,(ssize_t) c,exception);
443 SetPixelIndex(image,(Quantum) index,q);
444 SetPixelViaPixelInfo(image,image->colormap+index,q);
445 SetPixelAlpha(image,index == opacity ? TransparentAlpha : OpaqueAlpha,q);
cristy3ed852e2009-09-05 21:47:34 +0000446 x++;
cristyed231572011-07-14 02:18:59 +0000447 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000448 }
cristy891bc7f2013-08-01 00:25:02 +0000449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
450 break;
cristybb503372010-05-27 20:51:26 +0000451 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000452 break;
453 if (image->interlace == NoInterlace)
454 offset++;
455 else
cristy3ed852e2009-09-05 21:47:34 +0000456 {
cristy89ff2362013-09-16 22:31:00 +0000457 switch (pass)
cristy3ed852e2009-09-05 21:47:34 +0000458 {
cristy89ff2362013-09-16 22:31:00 +0000459 case 0:
460 default:
461 {
462 offset+=8;
463 break;
464 }
465 case 1:
466 {
467 offset+=8;
468 break;
469 }
470 case 2:
471 {
472 offset+=4;
473 break;
474 }
475 case 3:
476 {
477 offset+=2;
478 break;
479 }
cristy3ed852e2009-09-05 21:47:34 +0000480 }
cristy89ff2362013-09-16 22:31:00 +0000481 if ((pass == 0) && (offset >= (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000482 {
cristy89ff2362013-09-16 22:31:00 +0000483 pass++;
484 offset=4;
cristy3ed852e2009-09-05 21:47:34 +0000485 }
cristy89ff2362013-09-16 22:31:00 +0000486 if ((pass == 1) && (offset >= (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000487 {
cristy89ff2362013-09-16 22:31:00 +0000488 pass++;
489 offset=2;
cristy3ed852e2009-09-05 21:47:34 +0000490 }
cristy89ff2362013-09-16 22:31:00 +0000491 if ((pass == 2) && (offset >= (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000492 {
cristy89ff2362013-09-16 22:31:00 +0000493 pass++;
494 offset=1;
cristy3ed852e2009-09-05 21:47:34 +0000495 }
cristy89ff2362013-09-16 22:31:00 +0000496 }
cristy3ed852e2009-09-05 21:47:34 +0000497 }
498 lzw_info=RelinquishLZWInfo(lzw_info);
cristybb503372010-05-27 20:51:26 +0000499 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000500 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
501 return(MagickTrue);
502}
503
504/*
505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506% %
507% %
508% %
509% E n c o d e I m a g e %
510% %
511% %
512% %
513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514%
515% EncodeImage compresses an image via GIF-coding.
516%
517% The format of the EncodeImage method is:
518%
519% MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristybb503372010-05-27 20:51:26 +0000520% const size_t data_size)
cristy3ed852e2009-09-05 21:47:34 +0000521%
522% A description of each parameter follows:
523%
524% o image_info: the image info.
525%
526% o image: the address of a structure of type Image.
527%
528% o data_size: The number of bits in the compressed packet.
529%
530*/
531static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
cristyc82a27b2011-10-21 01:07:16 +0000532 const size_t data_size,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000533{
cristy5d89f432010-05-30 00:00:26 +0000534#define MaxCode(number_bits) ((one << (number_bits))-1)
cristy3ed852e2009-09-05 21:47:34 +0000535#define MaxHashTable 5003
536#define MaxGIFBits 12UL
537#define MaxGIFTable (1UL << MaxGIFBits)
538#define GIFOutputCode(code) \
539{ \
540 /* \
541 Emit a code. \
542 */ \
543 if (bits > 0) \
544 datum|=(code) << bits; \
545 else \
546 datum=code; \
547 bits+=number_bits; \
548 while (bits >= 8) \
549 { \
550 /* \
551 Add a character to current packet. \
552 */ \
553 packet[length++]=(unsigned char) (datum & 0xff); \
554 if (length >= 254) \
555 { \
556 (void) WriteBlobByte(image,(unsigned char) length); \
557 (void) WriteBlob(image,length,packet); \
558 length=0; \
559 } \
560 datum>>=8; \
561 bits-=8; \
562 } \
563 if (free_code > max_code) \
564 { \
565 number_bits++; \
566 if (number_bits == MaxGIFBits) \
567 max_code=MaxGIFTable; \
568 else \
569 max_code=MaxCode(number_bits); \
570 } \
571}
572
cristy4c08aed2011-07-01 19:47:50 +0000573 Quantum
cristy3ed852e2009-09-05 21:47:34 +0000574 index;
575
cristy3ed852e2009-09-05 21:47:34 +0000576 short
577 *hash_code,
578 *hash_prefix,
579 waiting_code;
580
cristybb503372010-05-27 20:51:26 +0000581 size_t
cristy3ed852e2009-09-05 21:47:34 +0000582 bits,
583 clear_code,
584 datum,
585 end_of_information_code,
586 free_code,
cristyebc891a2011-04-24 23:04:16 +0000587 length,
cristy3ed852e2009-09-05 21:47:34 +0000588 max_code,
589 next_pixel,
590 number_bits,
cristyebc891a2011-04-24 23:04:16 +0000591 one,
cristy3ed852e2009-09-05 21:47:34 +0000592 pass;
593
cristyebc891a2011-04-24 23:04:16 +0000594 ssize_t
595 displacement,
596 offset,
597 k,
598 y;
599
600 unsigned char
601 *packet,
602 *hash_suffix;
603
cristy3ed852e2009-09-05 21:47:34 +0000604 /*
605 Allocate encoder tables.
606 */
607 assert(image != (Image *) NULL);
cristy5d89f432010-05-30 00:00:26 +0000608 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000609 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
610 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
611 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
612 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
613 sizeof(*hash_suffix));
614 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
615 (hash_prefix == (short *) NULL) ||
616 (hash_suffix == (unsigned char *) NULL))
617 {
618 if (packet != (unsigned char *) NULL)
619 packet=(unsigned char *) RelinquishMagickMemory(packet);
620 if (hash_code != (short *) NULL)
621 hash_code=(short *) RelinquishMagickMemory(hash_code);
622 if (hash_prefix != (short *) NULL)
623 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
624 if (hash_suffix != (unsigned char *) NULL)
625 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
626 return(MagickFalse);
627 }
628 /*
629 Initialize GIF encoder.
630 */
cristy9e20c8f2015-02-02 01:12:54 +0000631 (void) ResetMagickMemory(hash_code,0,MaxHashTable*sizeof(*hash_code));
632 (void) ResetMagickMemory(hash_prefix,0,MaxHashTable*sizeof(*hash_prefix));
633 (void) ResetMagickMemory(hash_suffix,0,MaxHashTable*sizeof(*hash_suffix));
cristy3ed852e2009-09-05 21:47:34 +0000634 number_bits=data_size;
635 max_code=MaxCode(number_bits);
cristy5d89f432010-05-30 00:00:26 +0000636 clear_code=((short) one << (data_size-1));
cristy3ed852e2009-09-05 21:47:34 +0000637 end_of_information_code=clear_code+1;
638 free_code=clear_code+2;
639 length=0;
640 datum=0;
641 bits=0;
cristy3ed852e2009-09-05 21:47:34 +0000642 GIFOutputCode(clear_code);
643 /*
644 Encode pixels.
645 */
646 offset=0;
647 pass=0;
648 waiting_code=0;
cristybb503372010-05-27 20:51:26 +0000649 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000650 {
cristy4c08aed2011-07-01 19:47:50 +0000651 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +0100652 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000653
cristybb503372010-05-27 20:51:26 +0000654 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000655 x;
656
cristyc82a27b2011-10-21 01:07:16 +0000657 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000658 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000659 break;
cristy3ed852e2009-09-05 21:47:34 +0000660 if (y == 0)
cristy4c08aed2011-07-01 19:47:50 +0000661 {
662 waiting_code=(short) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +0000663 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000664 }
cristy15893a42010-11-20 18:57:15 +0000665 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000666 {
667 /*
668 Probe hash table.
669 */
cristy4c08aed2011-07-01 19:47:50 +0000670 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
cristyed231572011-07-14 02:18:59 +0000671 p+=GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +0000672 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000673 if (k >= MaxHashTable)
674 k-=MaxHashTable;
675 next_pixel=MagickFalse;
676 displacement=1;
677 if (hash_code[k] > 0)
678 {
679 if ((hash_prefix[k] == waiting_code) &&
680 (hash_suffix[k] == (unsigned char) index))
681 {
682 waiting_code=hash_code[k];
683 continue;
684 }
685 if (k != 0)
686 displacement=MaxHashTable-k;
687 for ( ; ; )
688 {
689 k-=displacement;
690 if (k < 0)
691 k+=MaxHashTable;
692 if (hash_code[k] == 0)
693 break;
694 if ((hash_prefix[k] == waiting_code) &&
695 (hash_suffix[k] == (unsigned char) index))
696 {
697 waiting_code=hash_code[k];
698 next_pixel=MagickTrue;
699 break;
700 }
701 }
cristycd8b3312013-12-22 01:51:11 +0000702 if (next_pixel != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000703 continue;
704 }
cristybb503372010-05-27 20:51:26 +0000705 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000706 if (free_code < MaxGIFTable)
707 {
708 hash_code[k]=(short) free_code++;
709 hash_prefix[k]=waiting_code;
710 hash_suffix[k]=(unsigned char) index;
711 }
712 else
713 {
714 /*
715 Fill the hash table with empty entries.
716 */
717 for (k=0; k < MaxHashTable; k++)
718 hash_code[k]=0;
719 /*
720 Reset compressor and issue a clear code.
721 */
722 free_code=clear_code+2;
723 GIFOutputCode(clear_code);
724 number_bits=data_size;
725 max_code=MaxCode(number_bits);
726 }
727 waiting_code=(short) index;
728 }
729 if (image_info->interlace == NoInterlace)
730 offset++;
731 else
732 switch (pass)
733 {
734 case 0:
735 default:
736 {
737 offset+=8;
cristybb503372010-05-27 20:51:26 +0000738 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000739 {
740 pass++;
741 offset=4;
742 }
743 break;
744 }
745 case 1:
746 {
747 offset+=8;
cristybb503372010-05-27 20:51:26 +0000748 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000749 {
750 pass++;
751 offset=2;
752 }
753 break;
754 }
755 case 2:
756 {
757 offset+=4;
cristybb503372010-05-27 20:51:26 +0000758 if (offset >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000759 {
760 pass++;
761 offset=1;
762 }
763 break;
764 }
765 case 3:
766 {
767 offset+=2;
768 break;
769 }
770 }
771 }
772 /*
773 Flush out the buffered code.
774 */
cristybb503372010-05-27 20:51:26 +0000775 GIFOutputCode((size_t) waiting_code);
cristy3ed852e2009-09-05 21:47:34 +0000776 GIFOutputCode(end_of_information_code);
777 if (bits > 0)
778 {
779 /*
780 Add a character to current packet.
781 */
782 packet[length++]=(unsigned char) (datum & 0xff);
783 if (length >= 254)
784 {
785 (void) WriteBlobByte(image,(unsigned char) length);
786 (void) WriteBlob(image,length,packet);
787 length=0;
788 }
789 }
790 /*
791 Flush accumulated data.
792 */
793 if (length > 0)
794 {
795 (void) WriteBlobByte(image,(unsigned char) length);
796 (void) WriteBlob(image,length,packet);
797 }
798 /*
799 Free encoder memory.
800 */
801 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
802 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
803 hash_code=(short *) RelinquishMagickMemory(hash_code);
804 packet=(unsigned char *) RelinquishMagickMemory(packet);
805 return(MagickTrue);
806}
807
808/*
809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810% %
811% %
812% %
813% I s G I F %
814% %
815% %
816% %
817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818%
819% IsGIF() returns MagickTrue if the image format type, identified by the
820% magick string, is GIF.
821%
822% The format of the IsGIF method is:
823%
824% MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
825%
826% A description of each parameter follows:
827%
828% o magick: compare image format pattern against these bytes.
829%
830% o length: Specifies the length of the magick string.
831%
832*/
833static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
834{
835 if (length < 4)
836 return(MagickFalse);
837 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
838 return(MagickTrue);
839 return(MagickFalse);
840}
841
842/*
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844% %
845% %
846% %
847+ R e a d B l o b B l o c k %
848% %
849% %
850% %
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852%
853% ReadBlobBlock() reads data from the image file and returns it. The
854% amount of data is determined by first reading a count byte. The number
855% of bytes read is returned.
856%
857% The format of the ReadBlobBlock method is:
858%
859% size_t ReadBlobBlock(Image *image,unsigned char *data)
860%
861% A description of each parameter follows:
862%
863% o image: the image.
864%
865% o data: Specifies an area to place the information requested from
866% the file.
867%
868*/
869static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
870{
871 ssize_t
872 count;
873
874 unsigned char
875 block_count;
876
877 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000878 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000879 assert(data != (unsigned char *) NULL);
880 count=ReadBlob(image,1,&block_count);
881 if (count != 1)
882 return(0);
cristy2c219b12012-07-12 11:13:04 +0000883 count=ReadBlob(image,(size_t) block_count,data);
884 if (count != (ssize_t) block_count)
885 return(0);
886 return(count);
cristy3ed852e2009-09-05 21:47:34 +0000887}
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*/
cristy018f07f2011-09-04 21:15:19 +0000915static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000916{
917 unsigned char
918 buffer[256],
919 length,
920 data_size;
921
922 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000923 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000924 if (image->debug != MagickFalse)
925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
926 if (ReadBlob(image,1,&data_size) != 1)
927 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
928 if (data_size > MaximumLZWBits)
929 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
930 if (ReadBlob(image,1,&length) != 1)
931 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
932 while (length != 0)
933 {
934 if (ReadBlob(image,length,buffer) != (ssize_t) length)
935 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
936 if (ReadBlob(image,1,&length) != 1)
937 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
938 }
939 return(MagickTrue);
940}
941
942static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
943{
944#define BitSet(byte,bit) (((byte) & (bit)) == (bit))
945#define LSBFirstOrder(x,y) (((y) << 8) | (x))
946
947 Image
cristy31197622013-03-06 02:07:54 +0000948 *image,
949 *meta_image;
cristy3ed852e2009-09-05 21:47:34 +0000950
951 int
952 number_extensionss=0;
953
cristy3ed852e2009-09-05 21:47:34 +0000954 MagickBooleanType
955 status;
956
957 RectangleInfo
958 page;
959
cristybb503372010-05-27 20:51:26 +0000960 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000961 i;
962
963 register unsigned char
964 *p;
965
cristyebc891a2011-04-24 23:04:16 +0000966 size_t
967 delay,
968 dispose,
glennrpb844bd52014-01-25 16:59:43 +0000969 duration,
cristyebc891a2011-04-24 23:04:16 +0000970 global_colors,
971 image_count,
972 iterations,
973 one;
974
cristy3ed852e2009-09-05 21:47:34 +0000975 ssize_t
cristyebc891a2011-04-24 23:04:16 +0000976 count,
977 opacity;
cristy3ed852e2009-09-05 21:47:34 +0000978
979 unsigned char
980 background,
981 c,
982 flag,
983 *global_colormap,
dirk9f3d8e32015-11-22 10:22:34 +0100984 buffer[257];
cristy3ed852e2009-09-05 21:47:34 +0000985
cristy3ed852e2009-09-05 21:47:34 +0000986 /*
987 Open image file.
988 */
989 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000990 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000991 if (image_info->debug != MagickFalse)
992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
993 image_info->filename);
994 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000995 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000996 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000997 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
998 if (status == MagickFalse)
999 {
1000 image=DestroyImageList(image);
1001 return((Image *) NULL);
1002 }
1003 /*
1004 Determine if this a GIF file.
1005 */
dirkb4979cb2015-11-22 10:01:27 +01001006 count=ReadBlob(image,6,buffer);
1007 if ((count != 6) || ((LocaleNCompare((char *) buffer,"GIF87",5) != 0) &&
1008 (LocaleNCompare((char *) buffer,"GIF89",5) != 0)))
cristy3ed852e2009-09-05 21:47:34 +00001009 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1010 page.width=ReadBlobLSBShort(image);
1011 page.height=ReadBlobLSBShort(image);
1012 flag=(unsigned char) ReadBlobByte(image);
1013 background=(unsigned char) ReadBlobByte(image);
1014 c=(unsigned char) ReadBlobByte(image); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001015 one=1;
1016 global_colors=one << (((size_t) flag & 0x07)+1);
cristy3ed852e2009-09-05 21:47:34 +00001017 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1018 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1019 if (global_colormap == (unsigned char *) NULL)
1020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1021 if (BitSet((int) flag,0x80) != 0)
cristycc3a7e72015-01-31 18:13:18 +00001022 {
1023 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1024 if (count != (ssize_t) (3*global_colors))
1025 {
1026 global_colormap=(unsigned char *) RelinquishMagickMemory(
1027 global_colormap);
1028 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
1029 }
1030 }
cristy3ed852e2009-09-05 21:47:34 +00001031 delay=0;
1032 dispose=0;
glennrpb844bd52014-01-25 16:59:43 +00001033 duration=0;
cristy3ed852e2009-09-05 21:47:34 +00001034 iterations=1;
1035 opacity=(-1);
1036 image_count=0;
cristy31197622013-03-06 02:07:54 +00001037 meta_image=AcquireImage(image_info,exception); /* metadata container */
cristy3ed852e2009-09-05 21:47:34 +00001038 for ( ; ; )
1039 {
1040 count=ReadBlob(image,1,&c);
1041 if (count != 1)
1042 break;
1043 if (c == (unsigned char) ';')
1044 break; /* terminator */
1045 if (c == (unsigned char) '!')
1046 {
1047 /*
1048 GIF Extension block.
1049 */
cristy3ed852e2009-09-05 21:47:34 +00001050 count=ReadBlob(image,1,&c);
1051 if (count != 1)
1052 {
1053 global_colormap=(unsigned char *) RelinquishMagickMemory(
1054 global_colormap);
1055 ThrowReaderException(CorruptImageError,
1056 "UnableToReadExtensionBlock");
1057 }
1058 switch (c)
1059 {
1060 case 0xf9:
1061 {
1062 /*
1063 Read graphics control extension.
1064 */
dirkb4979cb2015-11-22 10:01:27 +01001065 while (ReadBlobBlock(image,buffer) != 0) ;
1066 dispose=(size_t) (buffer[0] >> 2);
1067 delay=(size_t) ((buffer[2] << 8) | buffer[1]);
1068 if ((ssize_t) (buffer[0] & 0x01) == 0x01)
1069 opacity=(ssize_t) buffer[3];
cristy3ed852e2009-09-05 21:47:34 +00001070 break;
1071 }
1072 case 0xfe:
1073 {
1074 char
1075 *comments;
1076
cristyea43ca32012-05-20 23:47:26 +00001077 size_t
1078 length;
1079
cristy3ed852e2009-09-05 21:47:34 +00001080 /*
1081 Read comment extension.
1082 */
1083 comments=AcquireString((char *) NULL);
cristyea43ca32012-05-20 23:47:26 +00001084 for (length=0; ; length+=count)
cristy3ed852e2009-09-05 21:47:34 +00001085 {
dirkb4979cb2015-11-22 10:01:27 +01001086 count=(ssize_t) ReadBlobBlock(image,buffer);
cristy3ed852e2009-09-05 21:47:34 +00001087 if (count == 0)
1088 break;
dirkb4979cb2015-11-22 10:01:27 +01001089 buffer[count]='\0';
1090 (void) ConcatenateString(&comments,(const char *) buffer);
cristy3ed852e2009-09-05 21:47:34 +00001091 }
cristy31197622013-03-06 02:07:54 +00001092 (void) SetImageProperty(meta_image,"comment",comments,exception);
cristy3ed852e2009-09-05 21:47:34 +00001093 comments=DestroyString(comments);
1094 break;
1095 }
1096 case 0xff:
1097 {
cristy3ed852e2009-09-05 21:47:34 +00001098 MagickBooleanType
1099 loop;
1100
1101 /*
1102 Read Netscape Loop extension.
1103 */
1104 loop=MagickFalse;
dirkb4979cb2015-11-22 10:01:27 +01001105 if (ReadBlobBlock(image,buffer) != 0)
1106 loop=LocaleNCompare((char *) buffer,"NETSCAPE2.0",11) == 0 ?
cristy3ed852e2009-09-05 21:47:34 +00001107 MagickTrue : MagickFalse;
1108 if (loop != MagickFalse)
1109 {
dirkb4979cb2015-11-22 10:01:27 +01001110 while (ReadBlobBlock(image,buffer) != 0)
1111 iterations=(size_t) ((buffer[2] << 8) | buffer[1]);
cristy3ed852e2009-09-05 21:47:34 +00001112 break;
1113 }
1114 else
1115 {
1116 char
cristy151b66d2015-04-15 10:50:31 +00001117 name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001118
1119 int
1120 block_length,
1121 info_length,
1122 reserved_length;
1123
1124 MagickBooleanType
1125 i8bim,
1126 icc,
cristy03a398b2012-06-22 12:06:04 +00001127 iptc,
1128 magick;
cristy3ed852e2009-09-05 21:47:34 +00001129
1130 StringInfo
1131 *profile;
1132
1133 unsigned char
1134 *info;
1135
1136 /*
1137 Store GIF application extension as a generic profile.
1138 */
dirkb4979cb2015-11-22 10:01:27 +01001139 icc=LocaleNCompare((char *) buffer,"ICCRGBG1012",11) == 0 ?
cristy3ed852e2009-09-05 21:47:34 +00001140 MagickTrue : MagickFalse;
dirkb4979cb2015-11-22 10:01:27 +01001141 magick=LocaleNCompare((char *) buffer,"ImageMagick",11) == 0 ?
cristy03a398b2012-06-22 12:06:04 +00001142 MagickTrue : MagickFalse;
dirkb4979cb2015-11-22 10:01:27 +01001143 i8bim=LocaleNCompare((char *) buffer,"MGK8BIM0000",11) == 0 ?
cristy03a398b2012-06-22 12:06:04 +00001144 MagickTrue : MagickFalse;
dirkb4979cb2015-11-22 10:01:27 +01001145 iptc=LocaleNCompare((char *) buffer,"MGKIPTC0000",11) == 0 ?
cristy3ed852e2009-09-05 21:47:34 +00001146 MagickTrue : MagickFalse;
1147 number_extensionss++;
1148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1149 " Reading GIF application extension");
cristycc3a7e72015-01-31 18:13:18 +00001150 info=(unsigned char *) AcquireQuantumMemory(255UL,
1151 sizeof(*info));
cristybf6abbe2011-09-01 13:20:47 +00001152 if (info == (unsigned char *) NULL)
1153 ThrowReaderException(ResourceLimitError,
1154 "MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001155 reserved_length=255;
1156 for (info_length=0; ; )
1157 {
1158 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1159 if (block_length == 0)
1160 break;
1161 info_length+=block_length;
1162 if (info_length > (reserved_length-255))
1163 {
cristy7a0b87b2014-06-09 12:27:33 +00001164 reserved_length+=4096;
1165 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1166 reserved_length,sizeof(*info));
1167 if (info == (unsigned char *) NULL)
1168 ThrowReaderException(ResourceLimitError,
1169 "MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001170 }
1171 }
cristybf6abbe2011-09-01 13:20:47 +00001172 profile=BlobToStringInfo(info,(size_t) info_length);
1173 if (profile == (StringInfo *) NULL)
1174 ThrowReaderException(ResourceLimitError,
1175 "MemoryAllocationFailed");
cristycd8b3312013-12-22 01:51:11 +00001176 if (i8bim != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001177 (void) CopyMagickString(name,"8bim",sizeof(name));
cristycd8b3312013-12-22 01:51:11 +00001178 else if (icc != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001179 (void) CopyMagickString(name,"icc",sizeof(name));
cristycd8b3312013-12-22 01:51:11 +00001180 else if (iptc != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001181 (void) CopyMagickString(name,"iptc",sizeof(name));
cristycd8b3312013-12-22 01:51:11 +00001182 else if (magick != MagickFalse)
cristy03a398b2012-06-22 12:06:04 +00001183 {
1184 (void) CopyMagickString(name,"magick",sizeof(name));
cristye6693bb2015-02-11 01:24:42 +00001185 meta_image->gamma=StringToDouble((char *) info+6,
1186 (char **) NULL);
cristy03a398b2012-06-22 12:06:04 +00001187 }
cristy3ed852e2009-09-05 21:47:34 +00001188 else
cristyb51dff52011-05-19 16:55:47 +00001189 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
dirkb4979cb2015-11-22 10:01:27 +01001190 buffer);
cristy03a398b2012-06-22 12:06:04 +00001191 info=(unsigned char *) RelinquishMagickMemory(info);
1192 if (magick == MagickFalse)
cristy31197622013-03-06 02:07:54 +00001193 (void) SetImageProfile(meta_image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001194 profile=DestroyStringInfo(profile);
1195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1196 " profile name=%s",name);
1197 }
1198 break;
1199 }
1200 default:
1201 {
dirkb4979cb2015-11-22 10:01:27 +01001202 while (ReadBlobBlock(image,buffer) != 0) ;
cristy3ed852e2009-09-05 21:47:34 +00001203 break;
1204 }
1205 }
1206 }
1207 if (c != (unsigned char) ',')
1208 continue;
1209 if (image_count != 0)
1210 {
1211 /*
1212 Allocate next image structure.
1213 */
cristy9950d572011-10-01 18:22:35 +00001214 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001215 if (GetNextImageInList(image) == (Image *) NULL)
1216 {
1217 image=DestroyImageList(image);
1218 global_colormap=(unsigned char *) RelinquishMagickMemory(
1219 global_colormap);
1220 return((Image *) NULL);
1221 }
1222 image=SyncNextImageInList(image);
1223 }
1224 image_count++;
1225 /*
1226 Read image attributes.
1227 */
cristy11695122014-06-09 12:29:50 +00001228 meta_image->scene=image->scene;
cristycc3a7e72015-01-31 18:13:18 +00001229 (void) CloneImageProperties(image,meta_image);
cristyda060cb2013-03-30 01:32:15 +00001230 DestroyImageProperties(meta_image);
cristycc3a7e72015-01-31 18:13:18 +00001231 (void) CloneImageProfiles(image,meta_image);
cristyda060cb2013-03-30 01:32:15 +00001232 DestroyImageProfiles(meta_image);
cristy3ed852e2009-09-05 21:47:34 +00001233 image->storage_class=PseudoClass;
1234 image->compression=LZWCompression;
cristybb503372010-05-27 20:51:26 +00001235 page.x=(ssize_t) ReadBlobLSBShort(image);
1236 page.y=(ssize_t) ReadBlobLSBShort(image);
cristy3ed852e2009-09-05 21:47:34 +00001237 image->columns=ReadBlobLSBShort(image);
1238 image->rows=ReadBlobLSBShort(image);
1239 image->depth=8;
1240 flag=(unsigned char) ReadBlobByte(image);
cristyda060cb2013-03-30 01:32:15 +00001241 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
cristy96c4fcb2012-02-08 01:01:05 +00001242 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1243 ((size_t) (flag & 0x07)+1);
cristybb503372010-05-27 20:51:26 +00001244 if (opacity >= (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001245 opacity=(-1);
1246 image->page.width=page.width;
1247 image->page.height=page.height;
1248 image->page.y=page.y;
1249 image->page.x=page.x;
1250 image->delay=delay;
1251 image->ticks_per_second=100;
1252 image->dispose=(DisposeType) dispose;
1253 image->iterations=iterations;
cristyb0a657e2012-08-29 00:45:37 +00001254 image->alpha_trait=opacity >= 0 ? BlendPixelTrait : UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001255 delay=0;
1256 dispose=0;
cristy3ed852e2009-09-05 21:47:34 +00001257 if ((image->columns == 0) || (image->rows == 0))
1258 {
1259 global_colormap=(unsigned char *) RelinquishMagickMemory(
1260 global_colormap);
1261 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1262 }
1263 /*
1264 Inititialize colormap.
1265 */
cristy018f07f2011-09-04 21:15:19 +00001266 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001267 {
cristy7a0b87b2014-06-09 12:27:33 +00001268 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
cristy3ed852e2009-09-05 21:47:34 +00001269 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1270 }
1271 if (BitSet((int) flag,0x80) == 0)
1272 {
1273 /*
1274 Use global colormap.
1275 */
1276 p=global_colormap;
cristybb503372010-05-27 20:51:26 +00001277 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001278 {
cristyea43ca32012-05-20 23:47:26 +00001279 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1280 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1281 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001282 if (i == opacity)
1283 {
cristyea43ca32012-05-20 23:47:26 +00001284 image->colormap[i].alpha=(double) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001285 image->transparent_color=image->colormap[opacity];
1286 }
1287 }
cristycc3a7e72015-01-31 18:13:18 +00001288 image->background_color=image->colormap[MagickMin((ssize_t) background,
1289 (ssize_t) image->colors-1)];
cristy3ed852e2009-09-05 21:47:34 +00001290 }
1291 else
1292 {
1293 unsigned char
1294 *colormap;
1295
1296 /*
1297 Read local colormap.
1298 */
cristy96c4fcb2012-02-08 01:01:05 +00001299 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,3*
1300 sizeof(*colormap));
cristy3ed852e2009-09-05 21:47:34 +00001301 if (colormap == (unsigned char *) NULL)
1302 {
1303 global_colormap=(unsigned char *) RelinquishMagickMemory(
1304 global_colormap);
1305 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1306 }
1307 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1308 if (count != (ssize_t) (3*image->colors))
1309 {
1310 global_colormap=(unsigned char *) RelinquishMagickMemory(
1311 global_colormap);
1312 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1313 ThrowReaderException(CorruptImageError,
1314 "InsufficientImageDataInFile");
1315 }
1316 p=colormap;
cristybb503372010-05-27 20:51:26 +00001317 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001318 {
cristyea43ca32012-05-20 23:47:26 +00001319 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1320 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1321 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001322 if (i == opacity)
cristyea43ca32012-05-20 23:47:26 +00001323 image->colormap[i].alpha=(double) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001324 }
1325 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1326 }
cristy2691a3e2012-06-26 17:48:42 +00001327 if (image->gamma == 1.0)
1328 {
1329 for (i=0; i < (ssize_t) image->colors; i++)
cristy716daa82012-06-26 19:37:03 +00001330 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy2691a3e2012-06-26 17:48:42 +00001331 break;
1332 (void) SetImageColorspace(image,i == (ssize_t) image->colors ?
1333 GRAYColorspace : RGBColorspace,exception);
1334 }
cristy3ed852e2009-09-05 21:47:34 +00001335 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1336 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1337 break;
cristyacabb842014-12-14 23:36:33 +00001338 status=SetImageExtent(image,image->columns,image->rows,exception);
1339 if (status == MagickFalse)
1340 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +00001341 /*
1342 Decode image.
1343 */
1344 if (image_info->ping != MagickFalse)
cristy018f07f2011-09-04 21:15:19 +00001345 status=PingGIFImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001346 else
cristy018f07f2011-09-04 21:15:19 +00001347 status=DecodeImage(image,opacity,exception);
cristy3ed852e2009-09-05 21:47:34 +00001348 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1349 {
1350 global_colormap=(unsigned char *) RelinquishMagickMemory(
1351 global_colormap);
1352 ThrowReaderException(CorruptImageError,"CorruptImage");
1353 }
cristy7a0b87b2014-06-09 12:27:33 +00001354 duration+=image->delay*image->iterations;
cristy3ed852e2009-09-05 21:47:34 +00001355 if (image_info->number_scenes != 0)
1356 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1357 break;
1358 opacity=(-1);
cristy7a0b87b2014-06-09 12:27:33 +00001359 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-1,
1360 image->scene);
cristy3ed852e2009-09-05 21:47:34 +00001361 if (status == MagickFalse)
1362 break;
1363 }
glennrpb844bd52014-01-25 16:59:43 +00001364 image->duration=duration;
cristy31197622013-03-06 02:07:54 +00001365 meta_image=DestroyImage(meta_image);
cristy3ed852e2009-09-05 21:47:34 +00001366 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1367 if ((image->columns == 0) || (image->rows == 0))
1368 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1369 (void) CloseBlob(image);
1370 return(GetFirstImageInList(image));
1371}
1372
1373/*
1374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1375% %
1376% %
1377% %
1378% R e g i s t e r G I F I m a g e %
1379% %
1380% %
1381% %
1382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1383%
1384% RegisterGIFImage() adds properties for the GIF image format to
1385% the list of supported formats. The properties include the image format
1386% tag, a method to read and/or write the format, whether the format
1387% supports the saving of more than one frame to the same file or blob,
1388% whether the format supports native in-memory I/O, and a brief
1389% description of the format.
1390%
1391% The format of the RegisterGIFImage method is:
1392%
cristybb503372010-05-27 20:51:26 +00001393% size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001394%
1395*/
cristybb503372010-05-27 20:51:26 +00001396ModuleExport size_t RegisterGIFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001397{
1398 MagickInfo
1399 *entry;
1400
dirk06b627a2015-04-06 18:59:17 +00001401 entry=AcquireMagickInfo("GIF","GIF",
1402 "CompuServe graphics interchange format");
cristy3ed852e2009-09-05 21:47:34 +00001403 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1404 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1405 entry->magick=(IsImageFormatHandler *) IsGIF;
cristy12c3c032013-08-09 11:49:41 +00001406 entry->mime_type=ConstantString("image/gif");
cristy3ed852e2009-09-05 21:47:34 +00001407 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001408 entry=AcquireMagickInfo("GIF","GIF87",
1409 "CompuServe graphics interchange format");
cristy3ed852e2009-09-05 21:47:34 +00001410 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1411 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1412 entry->magick=(IsImageFormatHandler *) IsGIF;
dirk08e9a112015-02-22 01:51:41 +00001413 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001414 entry->version=ConstantString("version 87a");
cristy12c3c032013-08-09 11:49:41 +00001415 entry->mime_type=ConstantString("image/gif");
cristy3ed852e2009-09-05 21:47:34 +00001416 (void) RegisterMagickInfo(entry);
1417 return(MagickImageCoderSignature);
1418}
1419
1420/*
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422% %
1423% %
1424% %
1425% U n r e g i s t e r G I F I m a g e %
1426% %
1427% %
1428% %
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430%
1431% UnregisterGIFImage() removes format registrations made by the
1432% GIF module from the list of supported formats.
1433%
1434% The format of the UnregisterGIFImage method is:
1435%
1436% UnregisterGIFImage(void)
1437%
1438*/
1439ModuleExport void UnregisterGIFImage(void)
1440{
1441 (void) UnregisterMagickInfo("GIF");
1442 (void) UnregisterMagickInfo("GIF87");
1443}
1444
1445/*
1446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447% %
1448% %
1449% %
1450% W r i t e G I F I m a g e %
1451% %
1452% %
1453% %
1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455%
1456% WriteGIFImage() writes an image to a file in the Compuserve Graphics
1457% image format.
1458%
1459% The format of the WriteGIFImage method is:
1460%
cristy1e178e72011-08-28 19:44:34 +00001461% MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1462% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001463%
1464% A description of each parameter follows.
1465%
1466% o image_info: the image info.
1467%
1468% o image: The image.
1469%
cristy1e178e72011-08-28 19:44:34 +00001470% o exception: return any errors or warnings in this structure.
1471%
cristy3ed852e2009-09-05 21:47:34 +00001472*/
cristy1e178e72011-08-28 19:44:34 +00001473static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1474 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001475{
cristy3ed852e2009-09-05 21:47:34 +00001476 int
1477 c;
1478
cristy3ed852e2009-09-05 21:47:34 +00001479 ImageInfo
1480 *write_info;
1481
1482 InterlaceType
1483 interlace;
1484
1485 MagickBooleanType
1486 status;
1487
1488 MagickOffsetType
1489 scene;
1490
1491 RectangleInfo
1492 page;
1493
cristybb503372010-05-27 20:51:26 +00001494 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001495 i;
1496
1497 register unsigned char
1498 *q;
1499
1500 size_t
cristyebc891a2011-04-24 23:04:16 +00001501 bits_per_pixel,
1502 delay,
1503 length,
1504 one;
1505
1506 ssize_t
1507 j,
1508 opacity;
cristy3ed852e2009-09-05 21:47:34 +00001509
1510 unsigned char
1511 *colormap,
1512 *global_colormap;
1513
cristy3ed852e2009-09-05 21:47:34 +00001514 /*
1515 Open output image file.
1516 */
1517 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001518 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001519 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001520 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001521 if (image->debug != MagickFalse)
1522 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001523 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001524 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +00001525 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001526 if (status == MagickFalse)
1527 return(status);
1528 /*
1529 Allocate colormap.
1530 */
1531 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1532 sizeof(*global_colormap));
1533 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1534 if ((global_colormap == (unsigned char *) NULL) ||
1535 (colormap == (unsigned char *) NULL))
1536 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1537 for (i=0; i < 768; i++)
1538 colormap[i]=(unsigned char) 0;
1539 /*
1540 Write GIF header.
1541 */
1542 write_info=CloneImageInfo(image_info);
1543 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1544 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1545 else
1546 {
1547 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1548 write_info->adjoin=MagickFalse;
1549 }
1550 /*
1551 Determine image bounding box.
1552 */
1553 page.width=image->columns;
glennrpff6c41f2011-10-31 15:57:41 +00001554 if (image->page.width > page.width)
1555 page.width=image->page.width;
cristy3ed852e2009-09-05 21:47:34 +00001556 page.height=image->rows;
glennrpff6c41f2011-10-31 15:57:41 +00001557 if (image->page.height > page.height)
1558 page.height=image->page.height;
1559 page.x=image->page.x;
1560 page.y=image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001561 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1562 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1563 /*
1564 Write images to file.
1565 */
1566 interlace=write_info->interlace;
1567 if ((write_info->adjoin != MagickFalse) &&
1568 (GetNextImageInList(image) != (Image *) NULL))
1569 interlace=NoInterlace;
1570 scene=0;
cristy5d89f432010-05-30 00:00:26 +00001571 one=1;
cristy3ed852e2009-09-05 21:47:34 +00001572 do
1573 {
cristyaf8d3912014-02-21 14:50:33 +00001574 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001575 opacity=(-1);
cristy1e178e72011-08-28 19:44:34 +00001576 if (IsImageOpaque(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001577 {
1578 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +00001579 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +00001580 }
1581 else
1582 {
cristya19f1d72012-08-07 18:24:38 +00001583 double
cristy3ed852e2009-09-05 21:47:34 +00001584 alpha,
1585 beta;
1586
1587 /*
1588 Identify transparent colormap index.
1589 */
cristy21ef17b2015-02-09 22:59:26 +00001590 if ((image->storage_class == DirectClass) || (image->colors > 256))
1591 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +00001592 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001593 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00001594 {
1595 if (opacity < 0)
1596 {
1597 opacity=i;
1598 continue;
1599 }
cristy21ef17b2015-02-09 22:59:26 +00001600 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1601 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
cristy3ed852e2009-09-05 21:47:34 +00001602 if (alpha < beta)
1603 opacity=i;
1604 }
1605 if (opacity == -1)
1606 {
cristydef23e52015-01-22 11:52:01 +00001607 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +00001608 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001609 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00001610 {
1611 if (opacity < 0)
1612 {
1613 opacity=i;
1614 continue;
1615 }
cristy21ef17b2015-02-09 22:59:26 +00001616 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1617 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
cristy3ed852e2009-09-05 21:47:34 +00001618 if (alpha < beta)
1619 opacity=i;
1620 }
1621 }
1622 if (opacity >= 0)
1623 {
1624 image->colormap[opacity].red=image->transparent_color.red;
1625 image->colormap[opacity].green=image->transparent_color.green;
1626 image->colormap[opacity].blue=image->transparent_color.blue;
1627 }
1628 }
1629 if ((image->storage_class == DirectClass) || (image->colors > 256))
1630 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1631 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
cristy5d89f432010-05-30 00:00:26 +00001632 if ((one << bits_per_pixel) >= image->colors)
cristy3ed852e2009-09-05 21:47:34 +00001633 break;
1634 q=colormap;
cristybb503372010-05-27 20:51:26 +00001635 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001636 {
cristyea43ca32012-05-20 23:47:26 +00001637 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
1638 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
1639 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
cristy3ed852e2009-09-05 21:47:34 +00001640 }
cristy5d89f432010-05-30 00:00:26 +00001641 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00001642 {
1643 *q++=(unsigned char) 0x0;
1644 *q++=(unsigned char) 0x0;
1645 *q++=(unsigned char) 0x0;
1646 }
1647 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1648 (write_info->adjoin == MagickFalse))
1649 {
1650 /*
1651 Write global colormap.
1652 */
1653 c=0x80;
1654 c|=(8-1) << 4; /* color resolution */
1655 c|=(bits_per_pixel-1); /* size of global colormap */
1656 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001657 for (j=0; j < (ssize_t) image->colors; j++)
cristy101ab702011-10-13 13:06:32 +00001658 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
cristy3ed852e2009-09-05 21:47:34 +00001659 break;
1660 (void) WriteBlobByte(image,(unsigned char)
cristybb503372010-05-27 20:51:26 +00001661 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
cristy3ed852e2009-09-05 21:47:34 +00001662 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
cristy5d89f432010-05-30 00:00:26 +00001663 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001664 (void) WriteBlob(image,length,colormap);
1665 for (j=0; j < 768; j++)
1666 global_colormap[j]=colormap[j];
1667 }
1668 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1669 {
cristyca793d72015-01-23 13:03:55 +00001670 const char
1671 *value;
1672
cristy3ed852e2009-09-05 21:47:34 +00001673 /*
1674 Write graphics control extension.
1675 */
1676 (void) WriteBlobByte(image,(unsigned char) 0x21);
1677 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1678 (void) WriteBlobByte(image,(unsigned char) 0x04);
1679 c=image->dispose << 2;
1680 if (opacity >= 0)
1681 c|=0x01;
1682 (void) WriteBlobByte(image,(unsigned char) c);
cristybb503372010-05-27 20:51:26 +00001683 delay=(size_t) (100*image->delay/MagickMax((size_t)
cristy3ed852e2009-09-05 21:47:34 +00001684 image->ticks_per_second,1));
1685 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1686 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1687 0));
1688 (void) WriteBlobByte(image,(unsigned char) 0x00);
cristyca793d72015-01-23 13:03:55 +00001689 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00001690 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
cristyca793d72015-01-23 13:03:55 +00001691 (value != (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001692 {
cristyfa31e132012-05-31 14:12:41 +00001693 register const char
1694 *p;
cristy3ed852e2009-09-05 21:47:34 +00001695
1696 size_t
1697 count;
cristyfa31e132012-05-31 14:12:41 +00001698
cristy3ed852e2009-09-05 21:47:34 +00001699 /*
cristy03a398b2012-06-22 12:06:04 +00001700 Write comment extension.
1701 */
cristyfa31e132012-05-31 14:12:41 +00001702 (void) WriteBlobByte(image,(unsigned char) 0x21);
1703 (void) WriteBlobByte(image,(unsigned char) 0xfe);
cristyfa31e132012-05-31 14:12:41 +00001704 for (p=value; *p != '\0'; )
cristy3ed852e2009-09-05 21:47:34 +00001705 {
cristyfa31e132012-05-31 14:12:41 +00001706 count=MagickMin(strlen(p),255);
1707 (void) WriteBlobByte(image,(unsigned char) count);
1708 for (i=0; i < (ssize_t) count; i++)
1709 (void) WriteBlobByte(image,(unsigned char) *p++);
cristy3ed852e2009-09-05 21:47:34 +00001710 }
cristyfa31e132012-05-31 14:12:41 +00001711 (void) WriteBlobByte(image,(unsigned char) 0x00);
cristy3ed852e2009-09-05 21:47:34 +00001712 }
1713 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1714 (GetNextImageInList(image) != (Image *) NULL) &&
1715 (image->iterations != 1))
1716 {
1717 /*
1718 Write Netscape Loop extension.
1719 */
1720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1721 " Writing GIF Extension %s","NETSCAPE2.0");
1722 (void) WriteBlobByte(image,(unsigned char) 0x21);
1723 (void) WriteBlobByte(image,(unsigned char) 0xff);
1724 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1725 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1726 (void) WriteBlobByte(image,(unsigned char) 0x03);
1727 (void) WriteBlobByte(image,(unsigned char) 0x01);
1728 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1729 (void) WriteBlobByte(image,(unsigned char) 0x00);
1730 }
cristy03a398b2012-06-22 12:06:04 +00001731 if ((image->gamma != 1.0f/2.2f))
1732 {
1733 char
cristy151b66d2015-04-15 10:50:31 +00001734 attributes[MagickPathExtent];
cristy03a398b2012-06-22 12:06:04 +00001735
1736 ssize_t
cristycc3a7e72015-01-31 18:13:18 +00001737 count;
cristy03a398b2012-06-22 12:06:04 +00001738
1739 /*
1740 Write ImageMagick extension.
1741 */
1742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1743 " Writing GIF Extension %s","ImageMagick");
1744 (void) WriteBlobByte(image,(unsigned char) 0x21);
1745 (void) WriteBlobByte(image,(unsigned char) 0xff);
1746 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1747 (void) WriteBlob(image,11,(unsigned char *) "ImageMagick");
cristy151b66d2015-04-15 10:50:31 +00001748 count=FormatLocaleString(attributes,MagickPathExtent,"gamma=%g",
cristy03a398b2012-06-22 12:06:04 +00001749 image->gamma);
cristycc3a7e72015-01-31 18:13:18 +00001750 (void) WriteBlobByte(image,(unsigned char) count);
1751 (void) WriteBlob(image,(size_t) count,(unsigned char *) attributes);
cristy03a398b2012-06-22 12:06:04 +00001752 (void) WriteBlobByte(image,(unsigned char) 0x00);
1753 }
cristy3ed852e2009-09-05 21:47:34 +00001754 ResetImageProfileIterator(image);
1755 for ( ; ; )
1756 {
1757 char
1758 *name;
1759
1760 const StringInfo
1761 *profile;
1762
1763 name=GetNextImageProfile(image);
1764 if (name == (const char *) NULL)
1765 break;
1766 profile=GetImageProfile(image,name);
1767 if (profile != (StringInfo *) NULL)
1768 {
1769 if ((LocaleCompare(name,"ICC") == 0) ||
1770 (LocaleCompare(name,"ICM") == 0) ||
1771 (LocaleCompare(name,"IPTC") == 0) ||
1772 (LocaleCompare(name,"8BIM") == 0) ||
1773 (LocaleNCompare(name,"gif:",4) == 0))
1774 {
cristy3ed852e2009-09-05 21:47:34 +00001775 ssize_t
1776 offset;
1777
1778 unsigned char
1779 *datum;
1780
1781 datum=GetStringInfoDatum(profile);
1782 length=GetStringInfoLength(profile);
1783 (void) WriteBlobByte(image,(unsigned char) 0x21);
1784 (void) WriteBlobByte(image,(unsigned char) 0xff);
1785 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1786 if ((LocaleCompare(name,"ICC") == 0) ||
1787 (LocaleCompare(name,"ICM") == 0))
1788 {
1789 /*
1790 Write ICC extension.
1791 */
cristy03a398b2012-06-22 12:06:04 +00001792 (void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012");
cristy3ed852e2009-09-05 21:47:34 +00001793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyabcdc872014-08-30 22:53:43 +00001794 " Writing GIF Extension %s","ICCRGBG1012");
cristy3ed852e2009-09-05 21:47:34 +00001795 }
1796 else
1797 if ((LocaleCompare(name,"IPTC") == 0))
1798 {
1799 /*
cristy03a398b2012-06-22 12:06:04 +00001800 Write IPTC extension.
cristy3ed852e2009-09-05 21:47:34 +00001801 */
cristy03a398b2012-06-22 12:06:04 +00001802 (void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000");
cristy3ed852e2009-09-05 21:47:34 +00001803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyabcdc872014-08-30 22:53:43 +00001804 " Writing GIF Extension %s","MGKIPTC0000");
cristy3ed852e2009-09-05 21:47:34 +00001805 }
1806 else
1807 if ((LocaleCompare(name,"8BIM") == 0))
1808 {
1809 /*
cristy03a398b2012-06-22 12:06:04 +00001810 Write 8BIM extension.
cristy3ed852e2009-09-05 21:47:34 +00001811 */
1812 (void) WriteBlob(image,11,(unsigned char *)
1813 "MGK8BIM0000");
1814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " Writing GIF Extension %s","MGK8BIM0000");
1816 }
1817 else
1818 {
1819 char
cristy151b66d2015-04-15 10:50:31 +00001820 extension[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001821
cristy03a398b2012-06-22 12:06:04 +00001822 /*
1823 Write generic extension.
1824 */
cristy3ed852e2009-09-05 21:47:34 +00001825 (void) CopyMagickString(extension,name+4,
1826 sizeof(extension));
1827 (void) WriteBlob(image,11,(unsigned char *) extension);
1828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc3a7e72015-01-31 18:13:18 +00001829 " Writing GIF Extension %s",name);
cristy3ed852e2009-09-05 21:47:34 +00001830 }
1831 offset=0;
1832 while ((ssize_t) length > offset)
1833 {
1834 size_t
1835 block_length;
1836
1837 if ((length-offset) < 255)
1838 block_length=length-offset;
1839 else
1840 block_length=255;
1841 (void) WriteBlobByte(image,(unsigned char) block_length);
1842 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1843 offset+=(ssize_t) block_length;
1844 }
1845 (void) WriteBlobByte(image,(unsigned char) 0x00);
1846 }
1847 }
1848 }
1849 }
1850 (void) WriteBlobByte(image,','); /* image separator */
1851 /*
1852 Write the image header.
1853 */
1854 page.x=image->page.x;
1855 page.y=image->page.y;
1856 if ((image->page.width != 0) && (image->page.height != 0))
1857 page=image->page;
1858 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1859 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1860 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1861 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1862 c=0x00;
1863 if (interlace != NoInterlace)
1864 c|=0x40; /* pixel data is interlaced */
cristybb503372010-05-27 20:51:26 +00001865 for (j=0; j < (ssize_t) (3*image->colors); j++)
cristy3ed852e2009-09-05 21:47:34 +00001866 if (colormap[j] != global_colormap[j])
1867 break;
cristybb503372010-05-27 20:51:26 +00001868 if (j == (ssize_t) (3*image->colors))
cristy3ed852e2009-09-05 21:47:34 +00001869 (void) WriteBlobByte(image,(unsigned char) c);
1870 else
1871 {
1872 c|=0x80;
1873 c|=(bits_per_pixel-1); /* size of local colormap */
1874 (void) WriteBlobByte(image,(unsigned char) c);
cristy5d89f432010-05-30 00:00:26 +00001875 length=(size_t) (3*(one << bits_per_pixel));
cristy3ed852e2009-09-05 21:47:34 +00001876 (void) WriteBlob(image,length,colormap);
1877 }
1878 /*
1879 Write the image data.
1880 */
1881 c=(int) MagickMax(bits_per_pixel,2);
1882 (void) WriteBlobByte(image,(unsigned char) c);
cristyc82a27b2011-10-21 01:07:16 +00001883 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1884 exception);
cristy3ed852e2009-09-05 21:47:34 +00001885 if (status == MagickFalse)
1886 {
1887 global_colormap=(unsigned char *) RelinquishMagickMemory(
1888 global_colormap);
1889 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1890 write_info=DestroyImageInfo(write_info);
1891 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1892 }
1893 (void) WriteBlobByte(image,(unsigned char) 0x00);
1894 if (GetNextImageInList(image) == (Image *) NULL)
1895 break;
1896 image=SyncNextImageInList(image);
1897 scene++;
1898 status=SetImageProgress(image,SaveImagesTag,scene,
1899 GetImageListLength(image));
1900 if (status == MagickFalse)
1901 break;
1902 } while (write_info->adjoin != MagickFalse);
1903 (void) WriteBlobByte(image,';'); /* terminator */
1904 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1905 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1906 write_info=DestroyImageInfo(write_info);
1907 (void) CloseBlob(image);
1908 return(MagickTrue);
1909}