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