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