1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                     V   V  IIIII   CCCC   AAA   RRRR                        %
7%                     V   V    I    C      A   A  R   R                       %
8%                     V   V    I    C      AAAAA  RRRR                        %
9%                      V V     I    C      A   A  R R                         %
10%                       V    IIIII   CCCC  A   A  R  R                        %
11%                                                                             %
12%                                                                             %
13%                    Read/Write VICAR Rasterfile 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/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/colormap.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/colorspace-private.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/image.h"
54#include "MagickCore/image-private.h"
55#include "MagickCore/list.h"
56#include "MagickCore/magick.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/module.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/quantum-private.h"
62#include "MagickCore/quantum-private.h"
63#include "MagickCore/static.h"
64#include "MagickCore/string_.h"
65#include "MagickCore/string-private.h"
66
67/*
68  Forward declarations.
69*/
70static MagickBooleanType
71  WriteVICARImage(const ImageInfo *,Image *,ExceptionInfo *);
72
73/*
74%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75%                                                                             %
76%                                                                             %
77%                                                                             %
78%   I s V I C A R                                                             %
79%                                                                             %
80%                                                                             %
81%                                                                             %
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83%
84%  IsVICAR() returns MagickTrue if the image format type, identified by the
85%  magick string, is VICAR.
86%
87%  The format of the IsVICAR method is:
88%
89%      MagickBooleanType IsVICAR(const unsigned char *magick,
90%        const size_t length)
91%
92%  A description of each parameter follows:
93%
94%    o magick: compare image format pattern against these bytes.
95%
96%    o length: Specifies the length of the magick string.
97%
98*/
99static MagickBooleanType IsVICAR(const unsigned char *magick,
100  const size_t length)
101{
102  if (length < 14)
103    return(MagickFalse);
104  if (LocaleNCompare((const char *) magick,"LBLSIZE",7) == 0)
105    return(MagickTrue);
106  if (LocaleNCompare((const char *) magick,"NJPL1I",6) == 0)
107    return(MagickTrue);
108  if (LocaleNCompare((const char *) magick,"PDS_VERSION_ID",14) == 0)
109    return(MagickTrue);
110  return(MagickFalse);
111}
112
113/*
114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115%                                                                             %
116%                                                                             %
117%                                                                             %
118%   R e a d V I C A R I m a g e                                               %
119%                                                                             %
120%                                                                             %
121%                                                                             %
122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123%
124%  ReadVICARImage() reads a VICAR image file and returns it.  It
125%  allocates the memory necessary for the new Image structure and returns a
126%  pointer to the new image.
127%
128%  The format of the ReadVICARImage method is:
129%
130%      Image *ReadVICARImage(const ImageInfo *image_info,
131%        ExceptionInfo *exception)
132%
133%  A description of each parameter follows:
134%
135%    o image: Method ReadVICARImage returns a pointer to the image after
136%      reading.  A null image is returned if there is a memory shortage or if
137%      the image cannot be read.
138%
139%    o image_info: the image info.
140%
141%    o exception: return any errors or warnings in this structure.
142%
143%
144*/
145static Image *ReadVICARImage(const ImageInfo *image_info,
146  ExceptionInfo *exception)
147{
148  char
149    keyword[MagickPathExtent],
150    value[MagickPathExtent];
151
152  const unsigned char
153    *pixels;
154
155  Image
156    *image;
157
158  int
159    c;
160
161  MagickBooleanType
162    status,
163    value_expected;
164
165  QuantumInfo
166    *quantum_info;
167
168  QuantumType
169    quantum_type;
170
171  register Quantum
172    *q;
173
174  size_t
175    length;
176
177  ssize_t
178    count,
179    y;
180
181  /*
182    Open image file.
183  */
184  assert(image_info != (const ImageInfo *) NULL);
185  assert(image_info->signature == MagickCoreSignature);
186  if (image_info->debug != MagickFalse)
187    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
188      image_info->filename);
189  assert(exception != (ExceptionInfo *) NULL);
190  assert(exception->signature == MagickCoreSignature);
191  image=AcquireImage(image_info,exception);
192  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
193  if (status == MagickFalse)
194    {
195      image=DestroyImageList(image);
196      return((Image *) NULL);
197    }
198  /*
199    Decode image header.
200  */
201  c=ReadBlobByte(image);
202  count=1;
203  if (c == EOF)
204    {
205      image=DestroyImage(image);
206      return((Image *) NULL);
207    }
208  length=0;
209  image->columns=0;
210  image->rows=0;
211  while (isgraph(c) && ((image->columns == 0) || (image->rows == 0)))
212  {
213    if (isalnum(c) == MagickFalse)
214      {
215        c=ReadBlobByte(image);
216        count++;
217      }
218    else
219      {
220        register char
221          *p;
222
223        /*
224          Determine a keyword and its value.
225        */
226        p=keyword;
227        do
228        {
229          if ((size_t) (p-keyword) < (MagickPathExtent-1))
230            *p++=c;
231          c=ReadBlobByte(image);
232          count++;
233        } while (isalnum(c) || (c == '_'));
234        *p='\0';
235        value_expected=MagickFalse;
236        while ((isspace((int) ((unsigned char) c)) != 0) || (c == '='))
237        {
238          if (c == '=')
239            value_expected=MagickTrue;
240          c=ReadBlobByte(image);
241          count++;
242        }
243        if (value_expected == MagickFalse)
244          continue;
245        p=value;
246        while (isalnum(c))
247        {
248          if ((size_t) (p-value) < (MagickPathExtent-1))
249            *p++=c;
250          c=ReadBlobByte(image);
251          count++;
252        }
253        *p='\0';
254        /*
255          Assign a value to the specified keyword.
256        */
257        if (LocaleCompare(keyword,"Label_RECORDS") == 0)
258          length=(ssize_t) StringToLong(value);
259        if (LocaleCompare(keyword,"LBLSIZE") == 0)
260          length=(ssize_t) StringToLong(value);
261        if (LocaleCompare(keyword,"RECORD_BYTES") == 0)
262          image->columns=StringToUnsignedLong(value);
263        if (LocaleCompare(keyword,"NS") == 0)
264          image->columns=StringToUnsignedLong(value);
265        if (LocaleCompare(keyword,"LINES") == 0)
266          image->rows=StringToUnsignedLong(value);
267        if (LocaleCompare(keyword,"NL") == 0)
268          image->rows=StringToUnsignedLong(value);
269      }
270    while (isspace((int) ((unsigned char) c)) != 0)
271    {
272      c=ReadBlobByte(image);
273      count++;
274    }
275  }
276  while (count < (ssize_t) length)
277  {
278    c=ReadBlobByte(image);
279    if (c == EOF)
280      break;
281    count++;
282  }
283  if ((image->columns == 0) || (image->rows == 0))
284    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
285  image->depth=8;
286  if (image_info->ping != MagickFalse)
287    {
288      (void) CloseBlob(image);
289      return(GetFirstImageInList(image));
290    }
291  status=SetImageExtent(image,image->columns,image->rows,exception);
292  if (status == MagickFalse)
293    return(DestroyImageList(image));
294  /*
295    Read VICAR pixels.
296  */
297  (void) SetImageColorspace(image,GRAYColorspace,exception);
298  quantum_type=GrayQuantum;
299  quantum_info=AcquireQuantumInfo(image_info,image);
300  if (quantum_info == (QuantumInfo *) NULL)
301    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
302  length=GetQuantumExtent(image,quantum_info,quantum_type);
303  for (y=0; y < (ssize_t) image->rows; y++)
304  {
305    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
306    if (q == (Quantum *) NULL)
307      break;
308    pixels=(const unsigned char *) ReadBlobStream(image,length,
309      GetQuantumPixels(quantum_info),&count);
310    if (count != (ssize_t) length)
311      break;
312    (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
313      quantum_type,pixels,exception);
314    if (SyncAuthenticPixels(image,exception) == MagickFalse)
315      break;
316    status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
317      image->rows);
318    if (status == MagickFalse)
319      break;
320  }
321  SetQuantumImageType(image,quantum_type);
322  quantum_info=DestroyQuantumInfo(quantum_info);
323  if (EOFBlob(image) != MagickFalse)
324    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
325      image->filename);
326  (void) CloseBlob(image);
327  return(GetFirstImageInList(image));
328}
329
330/*
331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332%                                                                             %
333%                                                                             %
334%                                                                             %
335%   R e g i s t e r V I C A R I m a g e                                       %
336%                                                                             %
337%                                                                             %
338%                                                                             %
339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340%
341%  RegisterVICARImage() adds attributes for the VICAR image format to
342%  the list of supported formats.  The attributes include the image format
343%  tag, a method to read and/or write the format, whether the format
344%  supports the saving of more than one frame to the same file or blob,
345%  whether the format supports native in-memory I/O, and a brief
346%  description of the format.
347%
348%  The format of the RegisterVICARImage method is:
349%
350%      size_t RegisterVICARImage(void)
351%
352*/
353ModuleExport size_t RegisterVICARImage(void)
354{
355  MagickInfo
356    *entry;
357
358  entry=AcquireMagickInfo("VICAR","VICAR","VICAR rasterfile format");
359  entry->decoder=(DecodeImageHandler *) ReadVICARImage;
360  entry->encoder=(EncodeImageHandler *) WriteVICARImage;
361  entry->magick=(IsImageFormatHandler *) IsVICAR;
362  entry->flags^=CoderAdjoinFlag;
363  (void) RegisterMagickInfo(entry);
364  return(MagickImageCoderSignature);
365}
366
367/*
368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369%                                                                             %
370%                                                                             %
371%                                                                             %
372%   U n r e g i s t e r V I C A R I m a g e                                   %
373%                                                                             %
374%                                                                             %
375%                                                                             %
376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377%
378%  UnregisterVICARImage() removes format registrations made by the
379%  VICAR module from the list of supported formats.
380%
381%  The format of the UnregisterVICARImage method is:
382%
383%      UnregisterVICARImage(void)
384%
385*/
386ModuleExport void UnregisterVICARImage(void)
387{
388  (void) UnregisterMagickInfo("VICAR");
389}
390
391/*
392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393%                                                                             %
394%                                                                             %
395%                                                                             %
396%   W r i t e V I C A R I m a g e                                             %
397%                                                                             %
398%                                                                             %
399%                                                                             %
400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401%
402%  WriteVICARImage() writes an image in the VICAR rasterfile format.
403%  Vicar files contain a text header, followed by one or more planes of binary
404%  grayscale image data.  Vicar files are designed to allow many planes to be
405%  stacked together to form image cubes.  This method only writes a single
406%  grayscale plane.
407%
408%  WriteVICARImage was written contributed by gorelick@esther.la.asu.edu.
409%
410%  The format of the WriteVICARImage method is:
411%
412%      MagickBooleanType WriteVICARImage(const ImageInfo *image_info,
413%        Image *image,ExceptionInfo *exception)
414%
415%  A description of each parameter follows.
416%
417%    o image_info: the image info.
418%
419%    o image:  The image.
420%
421%    o exception: return any errors or warnings in this structure.
422%
423*/
424static MagickBooleanType WriteVICARImage(const ImageInfo *image_info,
425  Image *image,ExceptionInfo *exception)
426{
427  char
428    header[MagickPathExtent];
429
430  int
431    y;
432
433  MagickBooleanType
434    status;
435
436  QuantumInfo
437    *quantum_info;
438
439  register const Quantum
440    *p;
441
442  size_t
443    length;
444
445  ssize_t
446    count;
447
448  unsigned char
449    *pixels;
450
451  /*
452    Open output image file.
453  */
454  assert(image_info != (const ImageInfo *) NULL);
455  assert(image_info->signature == MagickCoreSignature);
456  assert(image != (Image *) NULL);
457  assert(image->signature == MagickCoreSignature);
458  if (image->debug != MagickFalse)
459    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
460  assert(exception != (ExceptionInfo *) NULL);
461  assert(exception->signature == MagickCoreSignature);
462  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
463  if (status == MagickFalse)
464    return(status);
465  (void) TransformImageColorspace(image,sRGBColorspace,exception);
466  /*
467    Write header.
468  */
469  (void) ResetMagickMemory(header,' ',MagickPathExtent);
470  (void) FormatLocaleString(header,MagickPathExtent,
471    "LBLSIZE=%.20g FORMAT='BYTE' TYPE='IMAGE' BUFSIZE=20000 DIM=2 EOL=0 "
472    "RECSIZE=%.20g ORG='BSQ' NL=%.20g NS=%.20g NB=1 N1=0 N2=0 N3=0 N4=0 NBB=0 "
473    "NLB=0 TASK='ImageMagick'",(double) MagickPathExtent,(double) image->columns,
474    (double) image->rows,(double) image->columns);
475  (void) WriteBlob(image,MagickPathExtent,(unsigned char *) header);
476  /*
477    Write VICAR pixels.
478  */
479  image->depth=8;
480  quantum_info=AcquireQuantumInfo(image_info,image);
481  if (quantum_info == (QuantumInfo *) NULL)
482    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
483  pixels=(unsigned char *) GetQuantumPixels(quantum_info);
484  for (y=0; y < (ssize_t) image->rows; y++)
485  {
486    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
487    if (p == (const Quantum *) NULL)
488      break;
489    length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
490      GrayQuantum,pixels,exception);
491    count=WriteBlob(image,length,pixels);
492    if (count != (ssize_t) length)
493      break;
494    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
495      image->rows);
496    if (status == MagickFalse)
497      break;
498  }
499  quantum_info=DestroyQuantumInfo(quantum_info);
500  (void) CloseBlob(image);
501  return(MagickTrue);
502}
503