blob: dddf02d652fa39c1f47339740f320fd2a8acb15f [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 %
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/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);
cristye1c94d92015-06-28 12:16:33 +0000238 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000239 if (image_info->debug != MagickFalse)
240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
241 image_info->filename);
242 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000243 assert(exception->signature == MagickCoreSignature);
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,
cristy151b66d2015-04-15 10:50:31 +0000317 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000318 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);
cristy100a0562014-04-18 01:27:37 +0000329 (void) close(unique_file);
cristy3ed852e2009-09-05 21:47:34 +0000330 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
331 if (status == MagickFalse)
332 {
333 char
334 *message;
335
cristy18c6c272011-09-23 14:40:37 +0000336 (void) remove_utf8(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000337 read_info=DestroyImageInfo(read_info);
338 message=GetExceptionMessage(errno);
cristyc82a27b2011-10-21 01:07:16 +0000339 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
340 "UnableToWriteFile","`%s': %s",image->filename,message);
cristy3ed852e2009-09-05 21:47:34 +0000341 message=DestroyString(message);
342 image=DestroyImageList(image);
343 return((Image *) NULL);
344 }
345 /*
346 Read JPEG image.
347 */
cristy16881e62012-05-06 14:41:29 +0000348 jpeg_image=ReadImage(read_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000349 (void) RelinquishUniqueFileResource(read_info->filename);
350 read_info=DestroyImageInfo(read_info);
cristy16881e62012-05-06 14:41:29 +0000351 if (jpeg_image == (Image *) NULL)
352 {
353 image=DestroyImageList(image);
354 return(jpeg_image);
355 }
cristy151b66d2015-04-15 10:50:31 +0000356 (void) CopyMagickString(jpeg_image->filename,image->filename,MagickPathExtent);
357 (void) CopyMagickString(jpeg_image->magick,image->magick,MagickPathExtent);
cristy16881e62012-05-06 14:41:29 +0000358 image=DestroyImageList(image);
359 image=jpeg_image;
cristy3ed852e2009-09-05 21:47:34 +0000360 /*
361 Correct image orientation.
362 */
363 flipped_image=FlipImage(image,exception);
cristybbfd4cd2010-04-13 21:54:39 +0000364 if (flipped_image != (Image *) NULL)
365 {
366 DuplicateBlob(flipped_image,image);
367 image=DestroyImage(image);
368 image=flipped_image;
369 }
370 return(GetFirstImageInList(image));
cristy3ed852e2009-09-05 21:47:34 +0000371}
372
373/*
374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375% %
376% %
377% %
378% R e g i s t e r S F W I m a g e %
379% %
380% %
381% %
382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383%
384% RegisterSFWImage() adds attributes for the SFW image format to
385% the list of supported formats. The attributes include the image format
386% tag, a method to read and/or write the format, whether the format
387% supports the saving of more than one frame to the same file or blob,
388% whether the format supports native in-memory I/O, and a brief
389% description of the format.
390%
391% The format of the RegisterSFWImage method is:
392%
cristybb503372010-05-27 20:51:26 +0000393% size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000394%
395*/
cristybb503372010-05-27 20:51:26 +0000396ModuleExport size_t RegisterSFWImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000397{
398 MagickInfo
399 *entry;
400
dirk06b627a2015-04-06 18:59:17 +0000401 entry=AcquireMagickInfo("SFW","SFW","Seattle Film Works");
cristy3ed852e2009-09-05 21:47:34 +0000402 entry->decoder=(DecodeImageHandler *) ReadSFWImage;
403 entry->magick=(IsImageFormatHandler *) IsSFW;
dirk08e9a112015-02-22 01:51:41 +0000404 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000405 (void) RegisterMagickInfo(entry);
406 return(MagickImageCoderSignature);
407}
408
409/*
410%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411% %
412% %
413% %
414% U n r e g i s t e r S F W I m a g e %
415% %
416% %
417% %
418%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419%
420% UnregisterSFWImage() removes format registrations made by the
421% SFW module from the list of supported formats.
422%
423% The format of the UnregisterSFWImage method is:
424%
425% UnregisterSFWImage(void)
426%
427*/
428ModuleExport void UnregisterSFWImage(void)
429{
430 (void) UnregisterMagickInfo("SFW");
431}