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