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