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