blob: e859f5a137be30002184074bd9ae555a39e40d30 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% SSSSS FFFFF W W %
7% SS F W W %
8% SSS FFF W W %
9% SS F W W W %
10% SSSSS F W W %
11% %
12% %
13% Read/Write ImageMagick Image 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/blob.h"
44#include "MagickCore/blob-private.h"
45#include "MagickCore/constitute.h"
46#include "MagickCore/exception.h"
47#include "MagickCore/exception-private.h"
48#include "MagickCore/image.h"
49#include "MagickCore/image-private.h"
50#include "MagickCore/list.h"
51#include "MagickCore/magick.h"
52#include "MagickCore/memory_.h"
53#include "MagickCore/resource_.h"
54#include "MagickCore/quantum-private.h"
55#include "MagickCore/static.h"
56#include "MagickCore/string_.h"
57#include "MagickCore/module.h"
58#include "MagickCore/transform.h"
59#include "MagickCore/utility.h"
cristy18c6c272011-09-23 14:40:37 +000060#include "MagickCore/utility-private.h"
cristy3ed852e2009-09-05 21:47:34 +000061
62/*
63%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64% %
65% %
66% %
67% I s S F W %
68% %
69% %
70% %
71%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72%
73% IsSFW() returns MagickTrue if the image format type, identified by the
74% magick string, is SFW.
75%
76% The format of the IsSFW method is:
77%
78% MagickBooleanType IsSFW(const unsigned char *magick,const size_t length)
79%
80% A description of each parameter follows:
81%
82% o magick: compare image format pattern against these bytes.
83%
84% o length: Specifies the length of the magick string.
85%
86*/
87static MagickBooleanType IsSFW(const unsigned char *magick,const size_t length)
88{
89 if (length < 5)
90 return(MagickFalse);
91 if (LocaleNCompare((const char *) magick,"SFW94",5) == 0)
92 return(MagickTrue);
93 return(MagickFalse);
94}
95
96/*
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98% %
99% %
100% %
101% R e a d S F W I m a g e %
102% %
103% %
104% %
105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106%
107% ReadSFWImage() reads a Seattle Film Works image file and returns it.
108% It allocates the memory necessary for the new Image structure and returns a
109% pointer to the new image.
110%
111% The format of the ReadSFWImage method is:
112%
113% Image *ReadSFWImage(const ImageInfo *image_info,ExceptionInfo *exception)
114%
115% A description of each parameter follows:
116%
117% o image_info: the image info.
118%
119% o exception: return any errors or warnings in this structure.
120%
121*/
122
cristy06beaa82012-05-11 11:18:16 +0000123static unsigned char *SFWScan(const unsigned char *p,const unsigned char *q,
cristy16881e62012-05-06 14:41:29 +0000124 const unsigned char *target,const size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000125{
cristybb503372010-05-27 20:51:26 +0000126 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000127 i;
128
cristy06beaa82012-05-11 11:18:16 +0000129 if ((p+length) < q)
130 while (p < q)
131 {
cristy2545d862012-05-26 19:23:56 +0000132 for (i=0; i < (ssize_t) length; i++)
cristy06beaa82012-05-11 11:18:16 +0000133 if (p[i] != target[i])
134 break;
cristy2545d862012-05-26 19:23:56 +0000135 if (i == (ssize_t) length)
cristy06beaa82012-05-11 11:18:16 +0000136 return((unsigned char *) p);
137 p++;
138 }
cristy3ed852e2009-09-05 21:47:34 +0000139 return((unsigned char *) NULL);
140}
141
142static void TranslateSFWMarker(unsigned char *marker)
143{
144 switch (marker[1])
145 {
146 case 0xc8: marker[1]=0xd8; break; /* soi */
147 case 0xd0: marker[1]=0xe0; break; /* app */
148 case 0xcb: marker[1]=0xdb; break; /* dqt */
149 case 0xa0: marker[1]=0xc0; break; /* sof */
150 case 0xa4: marker[1]=0xc4; break; /* sof */
151 case 0xca: marker[1]=0xda; break; /* sos */
152 case 0xc9: marker[1]=0xd9; break; /* eoi */
153 default: break;
154 }
155}
156
157static Image *ReadSFWImage(const ImageInfo *image_info,ExceptionInfo *exception)
158{
159 static unsigned char
160 HuffmanTable[] =
161 {
162 0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
163 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
165 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
166 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
167 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01,
168 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
169 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
170 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
171 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1,
172 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18,
173 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36,
174 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
175 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64,
176 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77,
177 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
178 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
179 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5,
180 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
181 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
182 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
183 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11,
184 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
185 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
186 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13,
187 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09,
188 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24,
189 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28,
190 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
191 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
192 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73,
193 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85,
194 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
195 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
196 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2,
197 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4,
198 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
199 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
200 0xF9, 0xFA
201 };
202
203 FILE
204 *file;
205
206 Image
207 *flipped_image,
cristy16881e62012-05-06 14:41:29 +0000208 *jpeg_image,
cristy3ed852e2009-09-05 21:47:34 +0000209 *image;
210
211 ImageInfo
212 *read_info;
213
214 int
215 unique_file;
216
217 MagickBooleanType
218 status;
219
220 register unsigned char
221 *header,
222 *data;
223
cristy95236b52009-12-30 21:56:45 +0000224 size_t
225 extent;
226
cristy3ed852e2009-09-05 21:47:34 +0000227 ssize_t
228 count;
229
230 unsigned char
231 *buffer,
232 *offset;
233
234 /*
235 Open image file.
236 */
237 assert(image_info != (const ImageInfo *) NULL);
238 assert(image_info->signature == MagickSignature);
239 if (image_info->debug != MagickFalse)
240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
241 image_info->filename);
242 assert(exception != (ExceptionInfo *) NULL);
243 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000244 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000245 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
246 if (status == MagickFalse)
247 {
248 image=DestroyImageList(image);
249 return((Image *) NULL);
250 }
251 /*
252 Read image into a buffer.
253 */
cristy16881e62012-05-06 14:41:29 +0000254 if (GetBlobSize(image) != (size_t) GetBlobSize(image))
255 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +0000256 buffer=(unsigned char *) AcquireQuantumMemory((size_t) GetBlobSize(image),
257 sizeof(*buffer));
258 if (buffer == (unsigned char *) NULL)
259 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
260 count=ReadBlob(image,(size_t) GetBlobSize(image),buffer);
cristy16881e62012-05-06 14:41:29 +0000261 if ((count != (ssize_t) GetBlobSize(image)) ||
262 (LocaleNCompare((char *) buffer,"SFW",3) != 0))
cristy3ed852e2009-09-05 21:47:34 +0000263 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
264 (void) CloseBlob(image);
cristy3ed852e2009-09-05 21:47:34 +0000265 /*
266 Find the start of the JFIF data
267 */
268 header=SFWScan(buffer,buffer+count-1,(const unsigned char *)
269 "\377\310\377\320",4);
270 if (header == (unsigned char *) NULL)
271 {
272 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
273 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
274 }
275 TranslateSFWMarker(header); /* translate soi and app tags */
276 TranslateSFWMarker(header+2);
277 (void) CopyMagickMemory(header+6,"JFIF\0\001\0",7); /* JFIF magic */
278 /*
279 Translate remaining markers.
280 */
281 offset=header+2;
cristy06beaa82012-05-11 11:18:16 +0000282 offset+=(((unsigned int) offset[2]) << 8)+offset[3]+2;
cristy3ed852e2009-09-05 21:47:34 +0000283 for ( ; ; )
284 {
cristy06beaa82012-05-11 11:18:16 +0000285 if ((offset+4) > (buffer+count-1))
286 {
287 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
288 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
289 }
cristy3ed852e2009-09-05 21:47:34 +0000290 TranslateSFWMarker(offset);
291 if (offset[1] == 0xda)
292 break;
cristy06beaa82012-05-11 11:18:16 +0000293 offset+=(((unsigned int) offset[2]) << 8)+offset[3]+2;
cristy3ed852e2009-09-05 21:47:34 +0000294 }
295 offset--;
296 data=SFWScan(offset,buffer+count-1,(const unsigned char *) "\377\311",2);
297 if (data == (unsigned char *) NULL)
298 {
299 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
300 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
301 }
302 TranslateSFWMarker(data++); /* translate eoi marker */
303 /*
304 Write JFIF file.
305 */
306 read_info=CloneImageInfo(image_info);
cristy98f91ec2011-02-03 01:15:48 +0000307 SetImageInfoBlob(read_info,(void *) NULL,0);
cristy3ed852e2009-09-05 21:47:34 +0000308 file=(FILE *) NULL;
309 unique_file=AcquireUniqueFileResource(read_info->filename);
310 if (unique_file != -1)
cristy18c6c272011-09-23 14:40:37 +0000311 file=fopen_utf8(read_info->filename,"wb");
cristy3ed852e2009-09-05 21:47:34 +0000312 if ((unique_file == -1) || (file == (FILE *) NULL))
313 {
314 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
315 read_info=DestroyImageInfo(read_info);
316 (void) CopyMagickString(image->filename,read_info->filename,
317 MaxTextExtent);
318 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
319 image->filename);
320 image=DestroyImageList(image);
321 return((Image *) NULL);
322 }
cristy95236b52009-12-30 21:56:45 +0000323 extent=fwrite(header,(size_t) (offset-header+1),1,file);
cristyda16f162011-02-19 23:52:17 +0000324 (void) extent;
cristy95236b52009-12-30 21:56:45 +0000325 extent=fwrite(HuffmanTable,1,sizeof(HuffmanTable)/sizeof(*HuffmanTable),file);
326 extent=fwrite(offset+1,(size_t) (data-offset),1,file);
cristy3ed852e2009-09-05 21:47:34 +0000327 status=ferror(file) == -1 ? MagickFalse : MagickTrue;
328 (void) fclose(file);
329 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
330 if (status == MagickFalse)
331 {
332 char
333 *message;
334
cristy18c6c272011-09-23 14:40:37 +0000335 (void) remove_utf8(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000336 read_info=DestroyImageInfo(read_info);
337 message=GetExceptionMessage(errno);
cristyc82a27b2011-10-21 01:07:16 +0000338 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
339 "UnableToWriteFile","`%s': %s",image->filename,message);
cristy3ed852e2009-09-05 21:47:34 +0000340 message=DestroyString(message);
341 image=DestroyImageList(image);
342 return((Image *) NULL);
343 }
344 /*
345 Read JPEG image.
346 */
cristy16881e62012-05-06 14:41:29 +0000347 jpeg_image=ReadImage(read_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000348 (void) RelinquishUniqueFileResource(read_info->filename);
349 read_info=DestroyImageInfo(read_info);
cristy16881e62012-05-06 14:41:29 +0000350 if (jpeg_image == (Image *) NULL)
351 {
352 image=DestroyImageList(image);
353 return(jpeg_image);
354 }
355 (void) CopyMagickString(jpeg_image->filename,image->filename,MaxTextExtent);
356 (void) CopyMagickString(jpeg_image->magick,image->magick,MaxTextExtent);
357 image=DestroyImageList(image);
358 image=jpeg_image;
cristy3ed852e2009-09-05 21:47:34 +0000359 /*
360 Correct image orientation.
361 */
362 flipped_image=FlipImage(image,exception);
cristybbfd4cd2010-04-13 21:54:39 +0000363 if (flipped_image != (Image *) NULL)
364 {
365 DuplicateBlob(flipped_image,image);
366 image=DestroyImage(image);
367 image=flipped_image;
368 }
369 return(GetFirstImageInList(image));
cristy3ed852e2009-09-05 21:47:34 +0000370}
371
372/*
373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374% %
375% %
376% %
377% R e g i s t e r S F W I m a g e %
378% %
379% %
380% %
381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382%
383% RegisterSFWImage() adds attributes for the SFW image format to
384% the list of supported formats. The attributes include the image format
385% tag, a method to read and/or write the format, whether the format
386% supports the saving of more than one frame to the same file or blob,
387% whether the format supports native in-memory I/O, and a brief
388% description of the format.
389%
390% The format of the RegisterSFWImage method is:
391%
cristybb503372010-05-27 20:51:26 +0000392% size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000393%
394*/
cristybb503372010-05-27 20:51:26 +0000395ModuleExport size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000396{
397 MagickInfo
398 *entry;
399
400 entry=SetMagickInfo("SFW");
401 entry->decoder=(DecodeImageHandler *) ReadSFWImage;
402 entry->magick=(IsImageFormatHandler *) IsSFW;
403 entry->adjoin=MagickFalse;
404 entry->description=ConstantString("Seattle Film Works");
405 entry->module=ConstantString("SFW");
406 (void) RegisterMagickInfo(entry);
407 return(MagickImageCoderSignature);
408}
409
410/*
411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412% %
413% %
414% %
415% U n r e g i s t e r S F W I m a g e %
416% %
417% %
418% %
419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420%
421% UnregisterSFWImage() removes format registrations made by the
422% SFW module from the list of supported formats.
423%
424% The format of the UnregisterSFWImage method is:
425%
426% UnregisterSFWImage(void)
427%
428*/
429ModuleExport void UnregisterSFWImage(void)
430{
431 (void) UnregisterMagickInfo("SFW");
432}