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