1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % IIIIIIIIII PPPPPPPP LL % 7 % II PP PP LL % 8 % II PP PP LL % 9 % II PP PP LL % 10 % II PPPPPPPP LL % 11 % II PP LL % 12 % II PP LL % 13 % IIIIIIIIII PP LLLLLLLL % 14 % % 15 % % 16 % % 17 % Read/Write Scanalytics IPLab Image Format % 18 % Sean Burke % 19 % 2008.05.07 % 20 % v 0.9 % 21 % % 22 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % 23 % dedicated to making software imaging solutions freely available. % 24 % % 25 % You may not use this file except in compliance with the License. You may % 26 % obtain a copy of the License at % 27 % % 28 % https://imagemagick.org/script/license.php % 29 % % 30 % Unless required by applicable law or agreed to in writing, software % 31 % distributed under the License is distributed on an "AS IS" BASIS, % 32 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 33 % See the License for the specific language governing permissions and % 34 % limitations under the License. % 35 % % 36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 % 38 % 39 */ 40 41 /* 42 Include declarations. 43 */ 44 #include "MagickCore/studio.h" 45 #include "MagickCore/blob.h" 46 #include "MagickCore/blob-private.h" 47 #include "MagickCore/cache.h" 48 #include "MagickCore/colorspace.h" 49 #include "MagickCore/colorspace-private.h" 50 #include "MagickCore/exception.h" 51 #include "MagickCore/exception-private.h" 52 #include "MagickCore/image.h" 53 #include "MagickCore/image-private.h" 54 #include "MagickCore/list.h" 55 #include "MagickCore/magick.h" 56 #include "MagickCore/memory_.h" 57 #include "MagickCore/monitor.h" 58 #include "MagickCore/monitor-private.h" 59 #include "MagickCore/option.h" 60 #include "MagickCore/property.h" 61 #include "MagickCore/quantum-private.h" 62 #include "MagickCore/static.h" 63 #include "MagickCore/string_.h" 64 #include "MagickCore/module.h" 65 66 /* 67 Tyedef declarations 68 */ 69 70 typedef struct _IPLInfo 71 { 72 unsigned int 73 tag, 74 size, 75 time, 76 z, 77 width, 78 height, 79 colors, 80 depth, 81 byteType; 82 } IPLInfo; 83 84 static MagickBooleanType 85 WriteIPLImage(const ImageInfo *,Image *,ExceptionInfo *); 86 87 /* 88 static void increase (void *pixel, int byteType){ 89 switch(byteType){ 90 case 0:(*((unsigned char *) pixel))++; break; 91 case 1:(*((signed int *) pixel))++; break; 92 case 2:(*((unsigned int *) pixel))++; break; 93 case 3:(*((signed long *) pixel))++; break; 94 default:(*((unsigned int *) pixel))++; break; 95 } 96 } 97 */ 98 99 /* 101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 102 % % 103 % % 104 % % 105 % I s I P L % 106 % % 107 % % 108 % % 109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 110 % 111 % IsIPL() returns MagickTrue if the image format type, identified by the 112 % magick string, is IPL. 113 % 114 % The format of the IsIPL method is: 115 % 116 % MagickBooleanType IsIPL(const unsigned char *magick,const size_t length) 117 % 118 % A description of each parameter follows: 119 % 120 % o magick: compare image format pattern against these bytes. 121 % 122 % o length: Specifies the length of the magick string. 123 % 124 */ 125 static MagickBooleanType IsIPL(const unsigned char *magick,const size_t length) 126 { 127 if (length < 4) 128 return(MagickFalse); 129 if (LocaleNCompare((const char *) magick,"data",4) == 0) 130 return(MagickTrue); 131 return(MagickFalse); 132 } 133 134 /* 136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 137 % % 138 % % 139 % % 140 % R e a d I P L I m a g e % 141 % % 142 % % 143 % % 144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 145 % 146 % ReadIPLImage() reads a Scanalytics IPLab image file and returns it. It 147 % allocates the memory necessary for the new Image structure and returns a 148 % pointer to the new image. 149 % 150 % According to the IPLab spec, the data is blocked out in five dimensions: 151 % { t, z, c, y, x }. When we return the image, the latter three are folded 152 % into the standard "Image" structure. The "scenes" (image_info->scene) 153 % correspond to the order: { {t0,z0}, {t0, z1}, ..., {t1,z0}, {t1,z1}... } 154 % The number of scenes is t*z. 155 % 156 % The format of the ReadIPLImage method is: 157 % 158 % Image *ReadIPLImage(const ImageInfo *image_info,ExceptionInfo *exception) 159 % 160 % A description of each parameter follows: 161 % 162 % o image_info: The image info. 163 % 164 % o exception: return any errors or warnings in this structure. 165 % 166 */ 167 168 static void SetHeaderFromIPL(Image *image, IPLInfo *ipl){ 169 image->columns = ipl->width; 170 image->rows = ipl->height; 171 image->depth = ipl->depth; 172 image->resolution.x = 1; 173 image->resolution.y = 1; 174 } 175 176 177 static Image *ReadIPLImage(const ImageInfo *image_info,ExceptionInfo *exception) 178 { 179 180 /* 181 Declare variables 182 */ 183 Image *image; 184 185 MagickBooleanType status; 186 register Quantum *q; 187 unsigned char magick[12], *pixels; 188 ssize_t count; 189 ssize_t y; 190 size_t t_count=0; 191 size_t length; 192 IPLInfo 193 ipl_info; 194 QuantumFormatType 195 quantum_format; 196 QuantumInfo 197 *quantum_info; 198 QuantumType 199 quantum_type; 200 201 size_t 202 extent; 203 204 /* 205 Open Image 206 */ 207 208 assert(image_info != (const ImageInfo *) NULL); 209 assert(image_info->signature == MagickCoreSignature); 210 if ( image_info->debug != MagickFalse) 211 (void) LogMagickEvent(TraceEvent, GetMagickModule(), "%s", 212 image_info->filename); 213 assert(exception != (ExceptionInfo *) NULL); 214 assert(exception->signature == MagickCoreSignature); 215 image=AcquireImage(image_info,exception); 216 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 217 if (status == MagickFalse) 218 { 219 image=DestroyImageList(image); 220 return((Image *) NULL); 221 } 222 223 /* 224 Read IPL image 225 */ 226 227 /* 228 Determine endianness 229 If we get back "iiii", we have LSB,"mmmm", MSB 230 */ 231 count=ReadBlob(image,4,magick); 232 if (count != 4) 233 ThrowReaderException(CorruptImageError, "ImproperImageHeader"); 234 if((LocaleNCompare((char *) magick,"iiii",4) == 0)) 235 image->endian=LSBEndian; 236 else{ 237 if((LocaleNCompare((char *) magick,"mmmm",4) == 0)) 238 image->endian=MSBEndian; 239 else{ 240 ThrowReaderException(CorruptImageError, "ImproperImageHeader"); 241 } 242 } 243 /* Skip o'er the next 8 bytes (garbage) */ 244 count=ReadBlob(image, 8, magick); 245 /* 246 Excellent, now we read the header unimpeded. 247 */ 248 count=ReadBlob(image,4,magick); 249 if((count != 4) || (LocaleNCompare((char *) magick,"data",4) != 0)) 250 ThrowReaderException(CorruptImageError, "ImproperImageHeader"); 251 ipl_info.size=ReadBlobLong(image); 252 ipl_info.width=ReadBlobLong(image); 253 ipl_info.height=ReadBlobLong(image); 254 if((ipl_info.width == 0UL) || (ipl_info.height == 0UL)) 255 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 256 ipl_info.colors=ReadBlobLong(image); 257 if(ipl_info.colors == 3){ SetImageColorspace(image,sRGBColorspace,exception);} 258 else { image->colorspace = GRAYColorspace; } 259 ipl_info.z=ReadBlobLong(image); 260 ipl_info.time=ReadBlobLong(image); 261 262 ipl_info.byteType=ReadBlobLong(image); 263 264 265 /* Initialize Quantum Info */ 266 267 switch (ipl_info.byteType) { 268 case 0: 269 ipl_info.depth=8; 270 quantum_format = UnsignedQuantumFormat; 271 break; 272 case 1: 273 ipl_info.depth=16; 274 quantum_format = SignedQuantumFormat; 275 break; 276 case 2: 277 ipl_info.depth=16; 278 quantum_format = UnsignedQuantumFormat; 279 break; 280 case 3: 281 ipl_info.depth=32; 282 quantum_format = SignedQuantumFormat; 283 break; 284 case 4: ipl_info.depth=32; 285 quantum_format = FloatingPointQuantumFormat; 286 break; 287 case 5: 288 ipl_info.depth=8; 289 quantum_format = UnsignedQuantumFormat; 290 break; 291 case 6: 292 ipl_info.depth=16; 293 quantum_format = UnsignedQuantumFormat; 294 break; 295 case 10: 296 ipl_info.depth=64; 297 quantum_format = FloatingPointQuantumFormat; 298 break; 299 default: 300 ipl_info.depth=16; 301 quantum_format = UnsignedQuantumFormat; 302 break; 303 } 304 extent=ipl_info.width*ipl_info.height*ipl_info.z*ipl_info.depth/8; 305 if (extent > GetBlobSize(image)) 306 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); 307 308 /* 309 Set number of scenes of image 310 */ 311 SetHeaderFromIPL(image, &ipl_info); 312 313 /* Thats all we need if we are pinging. */ 314 if (image_info->ping != MagickFalse) 315 { 316 (void) CloseBlob(image); 317 return(GetFirstImageInList(image)); 318 } 319 length=image->columns; 320 quantum_type=GetQuantumType(image,exception); 321 do 322 { 323 SetHeaderFromIPL(image, &ipl_info); 324 325 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) 326 if (image->scene >= (image_info->scene+image_info->number_scenes-1)) 327 break; 328 status=SetImageExtent(image,image->columns,image->rows,exception); 329 if (status == MagickFalse) 330 return(DestroyImageList(image)); 331 /* 332 printf("Length: %.20g, Memory size: %.20g\n", (double) length,(double) 333 image->depth); 334 */ 335 quantum_info=AcquireQuantumInfo(image_info,image); 336 if (quantum_info == (QuantumInfo *) NULL) 337 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 338 status=SetQuantumFormat(image,quantum_info,quantum_format); 339 if (status == MagickFalse) 340 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 341 pixels=(unsigned char *) GetQuantumPixels(quantum_info); 342 if(image->columns != ipl_info.width){ 343 /* 344 printf("Columns not set correctly! Wanted: %.20g, got: %.20g\n", 345 (double) ipl_info.width, (double) image->columns); 346 */ 347 } 348 349 /* 350 Covert IPL binary to pixel packets 351 */ 352 353 if(ipl_info.colors == 1){ 354 for(y = 0; y < (ssize_t) image->rows; y++){ 355 (void) ReadBlob(image, length*image->depth/8, pixels); 356 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 357 if (q == (Quantum *) NULL) 358 break; 359 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info, 360 GrayQuantum,pixels,exception); 361 if (SyncAuthenticPixels(image,exception) == MagickFalse) 362 break; 363 } 364 } 365 else{ 366 for(y = 0; y < (ssize_t) image->rows; y++){ 367 (void) ReadBlob(image, length*image->depth/8, pixels); 368 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 369 if (q == (Quantum *) NULL) 370 break; 371 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info, 372 RedQuantum,pixels,exception); 373 if (SyncAuthenticPixels(image,exception) == MagickFalse) 374 break; 375 } 376 for(y = 0; y < (ssize_t) image->rows; y++){ 377 (void) ReadBlob(image, length*image->depth/8, pixels); 378 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 379 if (q == (Quantum *) NULL) 380 break; 381 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info, 382 GreenQuantum,pixels,exception); 383 if (SyncAuthenticPixels(image,exception) == MagickFalse) 384 break; 385 } 386 for(y = 0; y < (ssize_t) image->rows; y++){ 387 (void) ReadBlob(image, length*image->depth/8, pixels); 388 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 389 if (q == (Quantum *) NULL) 390 break; 391 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info, 392 BlueQuantum,pixels,exception); 393 if (SyncAuthenticPixels(image,exception) == MagickFalse) 394 break; 395 } 396 } 397 SetQuantumImageType(image,quantum_type); 398 399 t_count++; 400 quantum_info = DestroyQuantumInfo(quantum_info); 401 402 if (EOFBlob(image) != MagickFalse) 403 { 404 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", 405 image->filename); 406 break; 407 } 408 if (t_count < ipl_info.z * ipl_info.time) 409 { 410 /* 411 Proceed to next image. 412 */ 413 AcquireNextImage(image_info,image,exception); 414 if (GetNextImageInList(image) == (Image *) NULL) 415 { 416 status=MagickFalse; 417 break; 418 } 419 image=SyncNextImageInList(image); 420 status=SetImageProgress(image,LoadImagesTag,TellBlob(image), 421 GetBlobSize(image)); 422 if (status == MagickFalse) 423 break; 424 } 425 } while (t_count < ipl_info.z*ipl_info.time); 426 CloseBlob(image); 427 if (status == MagickFalse) 428 return(DestroyImageList(image)); 429 return(GetFirstImageInList(image)); 430 } 431 432 /* 434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 435 % % 436 % % 437 % % 438 % R e g i s t e r I P L I m a g e % 439 % % 440 % % 441 % % 442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 443 % 444 % RegisterIPLImage() add attributes for the Scanalytics IPL image format to the 445 % list of supported formats. 446 % 447 % 448 */ 449 ModuleExport size_t RegisterIPLImage(void) 450 { 451 MagickInfo 452 *entry; 453 454 entry=AcquireMagickInfo("IPL","IPL","IPL Image Sequence"); 455 entry->decoder=(DecodeImageHandler *) ReadIPLImage; 456 entry->encoder=(EncodeImageHandler *) WriteIPLImage; 457 entry->magick=(IsImageFormatHandler *) IsIPL; 458 entry->flags|=CoderDecoderSeekableStreamFlag; 459 entry->flags|=CoderEndianSupportFlag; 460 (void) RegisterMagickInfo(entry); 461 return(MagickImageCoderSignature); 462 } 463 464 /* 466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 467 % % 468 % % 469 % % 470 % U n r e g i s t e r I P L I m a g e % 471 % % 472 % % 473 % % 474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 475 % 476 % UnregisterIPLImage() removes format registrations made by the 477 % IPL module from the list of supported formats. 478 % 479 % The format of the UnregisterIPLImage method is: 480 % 481 % UnregisterIPLImage(void) 482 % 483 */ 484 ModuleExport void UnregisterIPLImage(void) 485 { 486 (void) UnregisterMagickInfo("IPL"); 487 } 488 489 /* 490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 491 % % 492 % % 493 % % 494 % W r i t e I P L I m a g e % 495 % % 496 % % 497 % % 498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 499 % 500 % WriteIPLImage() writes an image to a file in Scanalytics IPLabimage format. 501 % 502 % The format of the WriteIPLImage method is: 503 % 504 % MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image) 505 % Image *image,ExceptionInfo *exception) 506 % 507 % A description of each parameter follows. 508 % 509 % o image_info: The image info. 510 % 511 % o image: The image. 512 % 513 % o exception: return any errors or warnings in this structure. 514 % 515 */ 516 static MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image, 517 ExceptionInfo *exception) 518 { 519 IPLInfo 520 ipl_info; 521 522 MagickBooleanType 523 status; 524 525 MagickOffsetType 526 scene; 527 528 register const Quantum 529 *p; 530 531 QuantumInfo 532 *quantum_info; 533 534 size_t 535 imageListLength; 536 537 ssize_t 538 y; 539 540 unsigned char 541 *pixels; 542 543 /* 544 Open output image file. 545 */ 546 assert(image_info != (const ImageInfo *) NULL); 547 assert(image_info->signature == MagickCoreSignature); 548 assert(image != (Image *) NULL); 549 assert(image->signature == MagickCoreSignature); 550 if (image->debug != MagickFalse) 551 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 552 assert(exception != (ExceptionInfo *) NULL); 553 assert(exception->signature == MagickCoreSignature); 554 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 555 if (status == MagickFalse) 556 return(status); 557 scene=0; 558 559 quantum_info=AcquireQuantumInfo(image_info,image); 560 if (quantum_info == (QuantumInfo *) NULL) 561 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 562 if ((quantum_info->format == UndefinedQuantumFormat) && 563 (IsHighDynamicRangeImage(image,exception) != MagickFalse)) 564 SetQuantumFormat(image,quantum_info,FloatingPointQuantumFormat); 565 switch(quantum_info->depth){ 566 case 8: 567 ipl_info.byteType = 0; 568 break; 569 case 16: 570 if(quantum_info->format == SignedQuantumFormat){ 571 ipl_info.byteType = 2; 572 } 573 else{ 574 ipl_info.byteType = 1; 575 } 576 break; 577 case 32: 578 if(quantum_info->format == FloatingPointQuantumFormat){ 579 ipl_info.byteType = 3; 580 } 581 else{ 582 ipl_info.byteType = 4; 583 } 584 break; 585 case 64: 586 ipl_info.byteType = 10; 587 break; 588 default: 589 ipl_info.byteType = 2; 590 break; 591 592 } 593 imageListLength=GetImageListLength(image); 594 ipl_info.z = (unsigned int) imageListLength; 595 /* There is no current method for detecting whether we have T or Z stacks */ 596 ipl_info.time = 1; 597 ipl_info.width = (unsigned int) image->columns; 598 ipl_info.height = (unsigned int) image->rows; 599 (void) TransformImageColorspace(image,sRGBColorspace,exception); 600 if(IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) { ipl_info.colors = 3; } 601 else{ ipl_info.colors = 1; } 602 603 ipl_info.size = (unsigned int) (28 + 604 ((image->depth)/8)*ipl_info.height*ipl_info.width*ipl_info.colors*ipl_info.z); 605 606 /* Ok! Calculations are done. Lets write this puppy down! */ 607 608 /* 609 Write IPL header. 610 */ 611 /* Shockingly (maybe not if you have used IPLab), IPLab itself CANNOT read MSBEndian 612 files! The reader above can, but they cannot. For compatability reasons, I will leave 613 the code in here, but it is all but useless if you want to use IPLab. */ 614 615 if(image_info->endian == MSBEndian) 616 (void) WriteBlob(image, 4, (const unsigned char *) "mmmm"); 617 else{ 618 image->endian = LSBEndian; 619 (void) WriteBlob(image, 4, (const unsigned char *) "iiii"); 620 } 621 (void) WriteBlobLong(image, 4); 622 (void) WriteBlob(image, 4, (const unsigned char *) "100f"); 623 (void) WriteBlob(image, 4, (const unsigned char *) "data"); 624 (void) WriteBlobLong(image, ipl_info.size); 625 (void) WriteBlobLong(image, ipl_info.width); 626 (void) WriteBlobLong(image, ipl_info.height); 627 (void) WriteBlobLong(image, ipl_info.colors); 628 if(image_info->adjoin == MagickFalse) 629 (void) WriteBlobLong(image, 1); 630 else 631 (void) WriteBlobLong(image, ipl_info.z); 632 (void) WriteBlobLong(image, ipl_info.time); 633 (void) WriteBlobLong(image, ipl_info.byteType); 634 635 do 636 { 637 /* 638 Convert MIFF to IPL raster pixels. 639 */ 640 pixels=(unsigned char *) GetQuantumPixels(quantum_info); 641 if(ipl_info.colors == 1){ 642 /* Red frame */ 643 for(y = 0; y < (ssize_t) ipl_info.height; y++){ 644 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 645 if (p == (const Quantum *) NULL) 646 break; 647 (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info, 648 GrayQuantum, pixels,exception); 649 (void) WriteBlob(image, image->columns*image->depth/8, pixels); 650 } 651 652 } 653 if(ipl_info.colors == 3){ 654 /* Red frame */ 655 for(y = 0; y < (ssize_t) ipl_info.height; y++){ 656 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 657 if (p == (const Quantum *) NULL) 658 break; 659 (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info, 660 RedQuantum, pixels,exception); 661 (void) WriteBlob(image, image->columns*image->depth/8, pixels); 662 } 663 664 /* Green frame */ 665 for(y = 0; y < (ssize_t) ipl_info.height; y++){ 666 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 667 if (p == (const Quantum *) NULL) 668 break; 669 (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info, 670 GreenQuantum, pixels,exception); 671 (void) WriteBlob(image, image->columns*image->depth/8, pixels); 672 } 673 /* Blue frame */ 674 for(y = 0; y < (ssize_t) ipl_info.height; y++){ 675 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 676 if (p == (const Quantum *) NULL) 677 break; 678 (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info, 679 BlueQuantum, pixels,exception); 680 (void) WriteBlob(image, image->columns*image->depth/8, pixels); 681 if (image->previous == (Image *) NULL) 682 { 683 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 684 image->rows); 685 if (status == MagickFalse) 686 break; 687 } 688 } 689 } 690 quantum_info=DestroyQuantumInfo(quantum_info); 691 if (GetNextImageInList(image) == (Image *) NULL) 692 break; 693 image=SyncNextImageInList(image); 694 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength); 695 if (status == MagickFalse) 696 break; 697 }while (image_info->adjoin != MagickFalse); 698 699 (void) WriteBlob(image, 4, (const unsigned char *) "fini"); 700 (void) WriteBlobLong(image, 0); 701 702 CloseBlob(image); 703 return(MagickTrue); 704 } 705