1/*
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%                                   Cristy                                    %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
21%  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*/
42#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"
60#include "MagickCore/utility-private.h"
61
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
123static unsigned char *SFWScan(const unsigned char *p,const unsigned char *q,
124  const unsigned char *target,const size_t length)
125{
126  register ssize_t
127    i;
128
129  if ((p+length) < q)
130    while (p < q)
131    {
132      for (i=0; i < (ssize_t) length; i++)
133        if (p[i] != target[i])
134          break;
135      if (i == (ssize_t) length)
136        return((unsigned char *) p);
137      p++;
138    }
139  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,
208    *jpeg_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
224  size_t
225    extent;
226
227  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 == MagickCoreSignature);
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 == MagickCoreSignature);
244  image=AcquireImage(image_info,exception);
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  if (GetBlobSize(image) != (size_t) GetBlobSize(image))
255    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
256  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);
261  if ((count != (ssize_t) GetBlobSize(image)) ||
262      (LocaleNCompare((char *) buffer,"SFW",3) != 0))
263    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
264  (void) CloseBlob(image);
265  /*
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;
282  offset+=(((unsigned int) offset[2]) << 8)+offset[3]+2;
283  for ( ; ; )
284  {
285    if ((offset+4) > (buffer+count-1))
286      {
287        buffer=(unsigned char *) RelinquishMagickMemory(buffer);
288        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
289      }
290    TranslateSFWMarker(offset);
291    if (offset[1] == 0xda)
292      break;
293    offset+=(((unsigned int) offset[2]) << 8)+offset[3]+2;
294  }
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);
307  SetImageInfoBlob(read_info,(void *) NULL,0);
308  file=(FILE *) NULL;
309  unique_file=AcquireUniqueFileResource(read_info->filename);
310  if (unique_file != -1)
311    file=fopen_utf8(read_info->filename,"wb");
312  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        MagickPathExtent);
318      ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
319        image->filename);
320      image=DestroyImageList(image);
321      return((Image *) NULL);
322    }
323  extent=fwrite(header,(size_t) (offset-header+1),1,file);
324  (void) extent;
325  extent=fwrite(HuffmanTable,1,sizeof(HuffmanTable)/sizeof(*HuffmanTable),file);
326  extent=fwrite(offset+1,(size_t) (data-offset),1,file);
327  status=ferror(file) == -1 ? MagickFalse : MagickTrue;
328  (void) fclose(file);
329  (void) close(unique_file);
330  buffer=(unsigned char *) RelinquishMagickMemory(buffer);
331  if (status == MagickFalse)
332    {
333      char
334        *message;
335
336      (void) remove_utf8(read_info->filename);
337      read_info=DestroyImageInfo(read_info);
338      message=GetExceptionMessage(errno);
339      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
340        "UnableToWriteFile","`%s': %s",image->filename,message);
341      message=DestroyString(message);
342      image=DestroyImageList(image);
343      return((Image *) NULL);
344    }
345  /*
346    Read JPEG image.
347  */
348  jpeg_image=ReadImage(read_info,exception);
349  (void) RelinquishUniqueFileResource(read_info->filename);
350  read_info=DestroyImageInfo(read_info);
351  if (jpeg_image == (Image *) NULL)
352    {
353      image=DestroyImageList(image);
354      return(jpeg_image);
355    }
356  (void) CopyMagickString(jpeg_image->filename,image->filename,MagickPathExtent);
357  (void) CopyMagickString(jpeg_image->magick,image->magick,MagickPathExtent);
358  image=DestroyImageList(image);
359  image=jpeg_image;
360  /*
361    Correct image orientation.
362  */
363  flipped_image=FlipImage(image,exception);
364  if (flipped_image != (Image *) NULL)
365    {
366      DuplicateBlob(flipped_image,image);
367      image=DestroyImage(image);
368      image=flipped_image;
369    }
370  return(GetFirstImageInList(image));
371}
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%
393%      size_t RegisterSFWImage(void)
394%
395*/
396ModuleExport size_t RegisterSFWImage(void)
397{
398  MagickInfo
399    *entry;
400
401  entry=AcquireMagickInfo("SFW","SFW","Seattle Film Works");
402  entry->decoder=(DecodeImageHandler *) ReadSFWImage;
403  entry->magick=(IsImageFormatHandler *) IsSFW;
404  entry->flags^=CoderAdjoinFlag;
405  (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}
432