blob: 46e6b6b998ce0f4d969f66d003f8435640f92dda [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
cristy3ed852e2009-09-05 21:47:34 +000060
61/*
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63% %
64% %
65% %
66% I s S F W %
67% %
68% %
69% %
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71%
72% IsSFW() returns MagickTrue if the image format type, identified by the
73% magick string, is SFW.
74%
75% The format of the IsSFW method is:
76%
77% MagickBooleanType IsSFW(const unsigned char *magick,const size_t length)
78%
79% A description of each parameter follows:
80%
81% o magick: compare image format pattern against these bytes.
82%
83% o length: Specifies the length of the magick string.
84%
85*/
86static MagickBooleanType IsSFW(const unsigned char *magick,const size_t length)
87{
88 if (length < 5)
89 return(MagickFalse);
90 if (LocaleNCompare((const char *) magick,"SFW94",5) == 0)
91 return(MagickTrue);
92 return(MagickFalse);
93}
94
95/*
96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97% %
98% %
99% %
100% R e a d S F W I m a g e %
101% %
102% %
103% %
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105%
106% ReadSFWImage() reads a Seattle Film Works image file and returns it.
107% It allocates the memory necessary for the new Image structure and returns a
108% pointer to the new image.
109%
110% The format of the ReadSFWImage method is:
111%
112% Image *ReadSFWImage(const ImageInfo *image_info,ExceptionInfo *exception)
113%
114% A description of each parameter follows:
115%
116% o image_info: the image info.
117%
118% o exception: return any errors or warnings in this structure.
119%
120*/
121
122static unsigned char *SFWScan(unsigned char *p,const unsigned char *q,
123 const unsigned char *target,const int length)
124{
cristybb503372010-05-27 20:51:26 +0000125 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000126 i;
127
128 for ( ; p < q; p++)
129 {
130 if (*p != *target)
131 continue;
132 if (length == 1)
133 return(p);
134 for (i=1; i < length; i++)
135 if (*(p+i) != *(target+i))
136 break;
137 if (i == length)
138 return(p);
139 }
140 return((unsigned char *) NULL);
141}
142
143static void TranslateSFWMarker(unsigned char *marker)
144{
145 switch (marker[1])
146 {
147 case 0xc8: marker[1]=0xd8; break; /* soi */
148 case 0xd0: marker[1]=0xe0; break; /* app */
149 case 0xcb: marker[1]=0xdb; break; /* dqt */
150 case 0xa0: marker[1]=0xc0; break; /* sof */
151 case 0xa4: marker[1]=0xc4; break; /* sof */
152 case 0xca: marker[1]=0xda; break; /* sos */
153 case 0xc9: marker[1]=0xd9; break; /* eoi */
154 default: break;
155 }
156}
157
158static Image *ReadSFWImage(const ImageInfo *image_info,ExceptionInfo *exception)
159{
160 static unsigned char
161 HuffmanTable[] =
162 {
163 0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
164 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
166 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
167 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
168 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01,
169 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
170 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
171 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
172 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1,
173 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18,
174 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36,
175 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
176 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64,
177 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77,
178 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
179 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
180 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5,
181 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
182 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
183 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
184 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11,
185 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
186 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
187 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13,
188 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09,
189 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24,
190 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28,
191 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
192 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
193 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73,
194 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85,
195 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
196 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
197 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2,
198 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4,
199 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
200 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
201 0xF9, 0xFA
202 };
203
204 FILE
205 *file;
206
207 Image
208 *flipped_image,
209 *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);
244 image=AcquireImage(image_info);
245 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 */
254 buffer=(unsigned char *) AcquireQuantumMemory((size_t) GetBlobSize(image),
255 sizeof(*buffer));
256 if (buffer == (unsigned char *) NULL)
257 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
258 count=ReadBlob(image,(size_t) GetBlobSize(image),buffer);
259 if ((count == 0) || (LocaleNCompare((char *) buffer,"SFW",3) != 0))
260 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
261 (void) CloseBlob(image);
262 image=DestroyImage(image);
263 /*
264 Find the start of the JFIF data
265 */
266 header=SFWScan(buffer,buffer+count-1,(const unsigned char *)
267 "\377\310\377\320",4);
268 if (header == (unsigned char *) NULL)
269 {
270 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
271 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
272 }
273 TranslateSFWMarker(header); /* translate soi and app tags */
274 TranslateSFWMarker(header+2);
275 (void) CopyMagickMemory(header+6,"JFIF\0\001\0",7); /* JFIF magic */
276 /*
277 Translate remaining markers.
278 */
279 offset=header+2;
280 offset+=(offset[2] << 8)+offset[3]+2;
281 for ( ; ; )
282 {
283 TranslateSFWMarker(offset);
284 if (offset[1] == 0xda)
285 break;
286 offset+=(offset[2] << 8)+offset[3]+2;
287 }
288 offset--;
289 data=SFWScan(offset,buffer+count-1,(const unsigned char *) "\377\311",2);
290 if (data == (unsigned char *) NULL)
291 {
292 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
293 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
294 }
295 TranslateSFWMarker(data++); /* translate eoi marker */
296 /*
297 Write JFIF file.
298 */
299 read_info=CloneImageInfo(image_info);
cristy98f91ec2011-02-03 01:15:48 +0000300 SetImageInfoBlob(read_info,(void *) NULL,0);
cristy3ed852e2009-09-05 21:47:34 +0000301 file=(FILE *) NULL;
302 unique_file=AcquireUniqueFileResource(read_info->filename);
303 if (unique_file != -1)
304 file=OpenMagickStream(read_info->filename,"wb");
305 if ((unique_file == -1) || (file == (FILE *) NULL))
306 {
307 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
308 read_info=DestroyImageInfo(read_info);
309 (void) CopyMagickString(image->filename,read_info->filename,
310 MaxTextExtent);
311 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
312 image->filename);
313 image=DestroyImageList(image);
314 return((Image *) NULL);
315 }
cristy95236b52009-12-30 21:56:45 +0000316 extent=fwrite(header,(size_t) (offset-header+1),1,file);
cristyda16f162011-02-19 23:52:17 +0000317 (void) extent;
cristy95236b52009-12-30 21:56:45 +0000318 extent=fwrite(HuffmanTable,1,sizeof(HuffmanTable)/sizeof(*HuffmanTable),file);
319 extent=fwrite(offset+1,(size_t) (data-offset),1,file);
cristy3ed852e2009-09-05 21:47:34 +0000320 status=ferror(file) == -1 ? MagickFalse : MagickTrue;
321 (void) fclose(file);
322 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
323 if (status == MagickFalse)
324 {
325 char
326 *message;
327
328 (void) remove(read_info->filename);
329 read_info=DestroyImageInfo(read_info);
330 message=GetExceptionMessage(errno);
331 (void) ThrowMagickException(&image->exception,GetMagickModule(),
332 FileOpenError,"UnableToWriteFile","`%s': %s",image->filename,message);
333 message=DestroyString(message);
334 image=DestroyImageList(image);
335 return((Image *) NULL);
336 }
337 /*
338 Read JPEG image.
339 */
340 image=ReadImage(read_info,exception);
341 (void) RelinquishUniqueFileResource(read_info->filename);
342 read_info=DestroyImageInfo(read_info);
343 if (image == (Image *) NULL)
344 return(GetFirstImageInList(image));
345 /*
346 Correct image orientation.
347 */
348 flipped_image=FlipImage(image,exception);
cristybbfd4cd2010-04-13 21:54:39 +0000349 if (flipped_image != (Image *) NULL)
350 {
351 DuplicateBlob(flipped_image,image);
352 image=DestroyImage(image);
353 image=flipped_image;
354 }
355 return(GetFirstImageInList(image));
cristy3ed852e2009-09-05 21:47:34 +0000356}
357
358/*
359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360% %
361% %
362% %
363% R e g i s t e r S F W I m a g e %
364% %
365% %
366% %
367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368%
369% RegisterSFWImage() adds attributes for the SFW image format to
370% the list of supported formats. The attributes include the image format
371% tag, a method to read and/or write the format, whether the format
372% supports the saving of more than one frame to the same file or blob,
373% whether the format supports native in-memory I/O, and a brief
374% description of the format.
375%
376% The format of the RegisterSFWImage method is:
377%
cristybb503372010-05-27 20:51:26 +0000378% size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000379%
380*/
cristybb503372010-05-27 20:51:26 +0000381ModuleExport size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000382{
383 MagickInfo
384 *entry;
385
386 entry=SetMagickInfo("SFW");
387 entry->decoder=(DecodeImageHandler *) ReadSFWImage;
388 entry->magick=(IsImageFormatHandler *) IsSFW;
389 entry->adjoin=MagickFalse;
390 entry->description=ConstantString("Seattle Film Works");
391 entry->module=ConstantString("SFW");
392 (void) RegisterMagickInfo(entry);
393 return(MagickImageCoderSignature);
394}
395
396/*
397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398% %
399% %
400% %
401% U n r e g i s t e r S F W I m a g e %
402% %
403% %
404% %
405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406%
407% UnregisterSFWImage() removes format registrations made by the
408% SFW module from the list of supported formats.
409%
410% The format of the UnregisterSFWImage method is:
411%
412% UnregisterSFWImage(void)
413%
414*/
415ModuleExport void UnregisterSFWImage(void)
416{
417 (void) UnregisterMagickInfo("SFW");
418}