]> 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   if((LocaleNCompare((char *) magick,"iiii",4) == 0))  
225     image->endian=LSBEndian;
226   else{
227     if((LocaleNCompare((char *) magick,"mmmm",4) == 0)) 
228       image->endian=MSBEndian;
229     else{
230       ThrowReaderException(CorruptImageError, "ImproperImageHeader");
231     }
232   }
233   /* Skip o'er the next 8 bytes (garbage) */
234   count=ReadBlob(image, 8, magick); 
235   /*
236    Excellent, now we read the header unimpeded.
237    */
238   count=ReadBlob(image,4,magick); 
239   if((LocaleNCompare((char *) magick,"data",4) != 0))  
240     ThrowReaderException(CorruptImageError, "ImproperImageHeader");
241   ipl_info.size=ReadBlobLong(image); 
242   ipl_info.width=ReadBlobLong(image); 
243   ipl_info.height=ReadBlobLong(image); 
244   if((ipl_info.width == 0UL) || (ipl_info.height == 0UL))
245     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
246   ipl_info.colors=ReadBlobLong(image); 
247   if(ipl_info.colors == 3){ image->colorspace=RGBColorspace;}
248   else { image->colorspace = GRAYColorspace; }
249   ipl_info.z=ReadBlobLong(image); 
250   ipl_info.time=ReadBlobLong(image); 
251
252   ipl_info.byteType=ReadBlobLong(image); 
253
254
255   /* Initialize Quantum Info */
256
257   switch (ipl_info.byteType) {
258     case 0: 
259       ipl_info.depth=8;
260       quantum_format = UnsignedQuantumFormat;
261       break;
262     case 1: 
263       ipl_info.depth=16;
264       quantum_format = SignedQuantumFormat;
265       break;
266     case 2: 
267       ipl_info.depth=16;
268       quantum_format = UnsignedQuantumFormat;
269       break;
270     case 3: 
271       ipl_info.depth=32;
272       quantum_format = SignedQuantumFormat;
273       break;
274     case 4: ipl_info.depth=32;
275       quantum_format = FloatingPointQuantumFormat;
276       break;
277     case 5: 
278       ipl_info.depth=8;
279       quantum_format = UnsignedQuantumFormat;
280       break;
281     case 6: 
282       ipl_info.depth=16;
283       quantum_format = UnsignedQuantumFormat;
284       break;
285     case 10:  
286       ipl_info.depth=64;
287       quantum_format = FloatingPointQuantumFormat;
288       break; 
289     default: 
290       ipl_info.depth=16;
291       quantum_format = UnsignedQuantumFormat;
292       break;
293   }
294
295   /*
296     Set number of scenes of image
297   */
298
299   SetHeaderFromIPL(image, &ipl_info);
300
301   /* Thats all we need if we are pinging. */
302   if (image_info->ping != MagickFalse)
303   {
304     (void) CloseBlob(image);
305     return(GetFirstImageInList(image));
306   }
307   length=image->columns;
308   quantum_type=GetQuantumType(image,exception);
309  do
310   {
311     SetHeaderFromIPL(image, &ipl_info);
312
313   if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
314       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
315         break;
316 /*
317    printf("Length: %.20g, Memory size: %.20g\n", (double) length,(double)
318      image->depth);
319 */
320      quantum_info=AcquireQuantumInfo(image_info,image);
321      if (quantum_info == (QuantumInfo *) NULL)
322        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
323      status=SetQuantumFormat(image,quantum_info,quantum_format);
324      if (status == MagickFalse)
325        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
326      pixels=GetQuantumPixels(quantum_info); 
327      if(image->columns != ipl_info.width){
328 /*
329      printf("Columns not set correctly!  Wanted: %.20g, got: %.20g\n",
330        (double) ipl_info.width, (double) image->columns);
331 */
332      }
333
334     /* 
335     Covert IPL binary to pixel packets
336      */
337     
338   if(ipl_info.colors == 1){
339       for(y = 0; y < (ssize_t) image->rows; y++){
340         (void) ReadBlob(image, length*image->depth/8, pixels);
341         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
342         if (q == (PixelPacket *) NULL)
343                 break;
344         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
345           GrayQuantum,pixels,exception);
346         if (SyncAuthenticPixels(image,exception) == MagickFalse)
347           break;
348     }
349   }
350   else{
351       for(y = 0; y < (ssize_t) image->rows; y++){
352         (void) ReadBlob(image, length*image->depth/8, pixels);
353         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
354         if (q == (PixelPacket *) NULL)
355                 break;
356         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
357           RedQuantum,pixels,exception);  
358         if (SyncAuthenticPixels(image,exception) == MagickFalse)
359           break;
360       }
361       for(y = 0; y < (ssize_t) image->rows; y++){
362         (void) ReadBlob(image, length*image->depth/8, pixels);
363         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
364         if (q == (PixelPacket *) NULL)
365                 break;
366         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
367           GreenQuantum,pixels,exception);
368         if (SyncAuthenticPixels(image,exception) == MagickFalse)
369           break;
370       }
371       for(y = 0; y < (ssize_t) image->rows; y++){
372         (void) ReadBlob(image, length*image->depth/8, pixels);
373         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
374         if (q == (PixelPacket *) NULL)
375                 break;
376         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
377           BlueQuantum,pixels,exception);
378         if (SyncAuthenticPixels(image,exception) == MagickFalse)
379           break;
380       }
381    }
382    SetQuantumImageType(image,quantum_type);
383  
384     t_count++;
385   quantum_info = DestroyQuantumInfo(quantum_info);
386
387     if (EOFBlob(image) != MagickFalse)
388     {
389       ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
390                  image->filename);
391       break;
392     }
393    if(t_count < ipl_info.z * ipl_info.time){
394       /*
395        Proceed to next image.
396        */
397       AcquireNextImage(image_info, image);
398       if (GetNextImageInList(image) == (Image *) NULL)
399       {
400         image=DestroyImageList(image);
401         return((Image *) NULL);
402       }
403       image=SyncNextImageInList(image); 
404       status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
405         GetBlobSize(image));
406       if (status == MagickFalse)
407         break;
408     }
409   } while (t_count < ipl_info.z*ipl_info.time);
410   CloseBlob(image);
411   return(GetFirstImageInList(image));
412 }
413
414 /*
415  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416  %                                                                             %
417  %                                                                             %
418  %                                                                             %
419  %   R e g i s t e r I P L I m a g e                                           %
420  %                                                                             %
421  %                                                                             %
422  %                                                                             %
423  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424  %
425  % RegisterIPLImage() add attributes for the Scanalytics IPL image format to the
426  % list of supported formats.  
427  %
428  %
429  */
430 ModuleExport size_t RegisterIPLImage(void)
431 {
432   MagickInfo
433     *entry;
434   
435   entry=SetMagickInfo("IPL");
436   entry->decoder=(DecodeImageHandler *) ReadIPLImage;
437   entry->encoder=(EncodeImageHandler *) WriteIPLImage;
438   entry->magick=(IsImageFormatHandler *) IsIPL;
439   entry->adjoin=MagickTrue;
440   entry->description=ConstantString("IPL Image Sequence");
441   entry->module=ConstantString("IPL");
442   entry->endian_support=MagickTrue;
443   (void) RegisterMagickInfo(entry);
444   return(MagickImageCoderSignature);
445 }
446
447 /*
448  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449  %                                                                             %
450  %                                                                             %
451  %                                                                             %
452  %   U n r e g i s t e r I P L I m a g e                                       %
453  %                                                                             %
454  %                                                                             %
455  %                                                                             %
456  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457  %
458  %  UnregisterIPLImage() removes format registrations made by the
459  %  IPL module from the list of supported formats.
460  %
461  %  The format of the UnregisterIPLImage method is:
462  %
463  %      UnregisterIPLImage(void)
464  %
465  */
466 ModuleExport void UnregisterIPLImage(void)
467 {
468   (void) UnregisterMagickInfo("IPL");
469 }
470
471 /*
472  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473  %                                                                             %
474  %                                                                             %
475  %                                                                             %
476  %   W r i t e I P L I m a g e                                                 %
477  %                                                                             %
478  %                                                                             %
479  %                                                                             %
480  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481  %
482  %  WriteIPLImage() writes an image to a file in Scanalytics IPLabimage format.
483  %
484  %  The format of the WriteIPLImage method is:
485  %
486  %      MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image)
487  %
488  %  A description of each parameter follows.
489  %
490  %    o image_info: The image info.
491  %
492  %    o image:  The image.
493  %
494  */
495
496 static MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image)
497 {
498   ExceptionInfo
499     *exception;
500
501   MagickBooleanType
502     status;
503   
504   MagickOffsetType
505     scene;
506   
507   register const PixelPacket
508     *p;
509
510   unsigned char
511   *pixels;
512  
513   ssize_t
514     y;
515   
516   IPLInfo
517     ipl_info;
518
519   QuantumInfo
520     *quantum_info;
521
522    /*
523     Open output image file.
524   */
525   assert(image_info != (const ImageInfo *) NULL);
526   assert(image_info->signature == MagickSignature);
527   assert(image != (Image *) NULL);
528   assert(image->signature == MagickSignature);
529   if (image->debug != MagickFalse)
530     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
531   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
532   if (status == MagickFalse)
533     return(status);
534   scene=0;
535   
536
537   quantum_info=AcquireQuantumInfo(image_info, image);
538   if ((quantum_info->format == UndefinedQuantumFormat) &&
539       (IsHighDynamicRangeImage(image,&image->exception) != MagickFalse))
540     SetQuantumFormat(image,quantum_info,FloatingPointQuantumFormat);
541   switch(quantum_info->depth){
542   case 8: 
543     ipl_info.byteType = 0;
544     break;
545   case 16:
546     if(quantum_info->format == SignedQuantumFormat){
547       ipl_info.byteType = 2;
548     }
549     else{
550       ipl_info.byteType = 1;
551     }
552     break;
553   case 32:
554     if(quantum_info->format == FloatingPointQuantumFormat){
555       ipl_info.byteType = 3;
556     }
557     else{
558       ipl_info.byteType = 4;
559     }
560     break;
561   case 64:
562     ipl_info.byteType = 10;
563     break;
564   default: 
565     ipl_info.byteType = 2; 
566     break;
567     
568   }
569   ipl_info.z = (unsigned int) GetImageListLength(image);
570   /* There is no current method for detecting whether we have T or Z stacks */
571   ipl_info.time = 1;
572   ipl_info.width = (unsigned int) image->columns;
573   ipl_info.height = (unsigned int) image->rows;
574   
575   if (image->colorspace != RGBColorspace)
576     (void) TransformImageColorspace(image,RGBColorspace);
577   
578   if(image->colorspace == RGBColorspace) { ipl_info.colors = 3; }
579   else{ ipl_info.colors = 1; }
580   
581   ipl_info.size = (unsigned int) (28 + 
582     ((image->depth)/8)*ipl_info.height*ipl_info.width*ipl_info.colors*ipl_info.z);
583   
584   /* Ok!  Calculations are done.  Lets write this puppy down! */
585   
586   /*
587     Write IPL header.
588   */
589   /* Shockingly (maybe not if you have used IPLab),  IPLab itself CANNOT read MSBEndian
590   files!   The reader above can, but they cannot.  For compatability reasons, I will leave
591   the code in here, but it is all but useless if you want to use IPLab. */
592
593   if(image_info->endian == MSBEndian)
594     (void) WriteBlob(image, 4, (const unsigned char *) "mmmm");
595   else{
596     image->endian = LSBEndian;
597     (void) WriteBlob(image, 4, (const unsigned char *) "iiii");
598   }
599   (void) WriteBlobLong(image, 4);
600   (void) WriteBlob(image, 4, (const unsigned char *) "100f");
601   (void) WriteBlob(image, 4, (const unsigned char *) "data");
602   (void) WriteBlobLong(image, ipl_info.size);
603   (void) WriteBlobLong(image, ipl_info.width); 
604   (void) WriteBlobLong(image, ipl_info.height);
605   (void) WriteBlobLong(image, ipl_info.colors);
606   if(image_info->adjoin == MagickFalse)
607   (void) WriteBlobLong(image, 1);
608   else
609   (void) WriteBlobLong(image, ipl_info.z);
610   (void) WriteBlobLong(image, ipl_info.time);
611   (void) WriteBlobLong(image, ipl_info.byteType);
612   
613   exception=(&image->exception);
614   do
615     {
616       /*
617   Convert MIFF to IPL raster pixels.
618       */
619       pixels=GetQuantumPixels(quantum_info);
620   if(ipl_info.colors == 1){
621   /* Red frame */
622   for(y = 0; y < (ssize_t) ipl_info.height; y++){
623     p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
624     if (p == (PixelPacket *) NULL)
625       break;
626       (void) ExportQuantumPixels(image,(const CacheView *) NULL, quantum_info,
627         GrayQuantum, pixels,&image->exception);
628       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
629   }
630
631 }
632   if(ipl_info.colors == 3){
633   /* Red frame */
634   for(y = 0; y < (ssize_t) ipl_info.height; y++){
635     p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
636     if (p == (PixelPacket *) NULL)
637       break;
638       (void) ExportQuantumPixels(image,(const CacheView *) NULL, quantum_info,
639         RedQuantum, pixels,&image->exception);
640       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
641   }
642
643     /* Green frame */
644     for(y = 0; y < (ssize_t) ipl_info.height; y++){
645       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
646       if (p == (PixelPacket *) NULL)
647         break;
648         (void) ExportQuantumPixels(image,(const CacheView *) NULL, quantum_info,
649           GreenQuantum, pixels,&image->exception);
650         (void) WriteBlob(image, image->columns*image->depth/8, pixels);
651     }
652     /* Blue frame */
653     for(y = 0; y < (ssize_t) ipl_info.height; y++){
654       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
655       if (p == (PixelPacket *) NULL)
656         break;
657       (void) ExportQuantumPixels(image,(const CacheView *) NULL, quantum_info,
658         BlueQuantum, pixels,&image->exception);
659       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
660       if (image->previous == (Image *) NULL)
661         {
662           status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
663                 image->rows);
664           if (status == MagickFalse)
665             break;
666         }
667     }
668   }
669   quantum_info=DestroyQuantumInfo(quantum_info);
670       if (GetNextImageInList(image) == (Image *) NULL)
671   break;
672       image=SyncNextImageInList(image);
673       status=SetImageProgress(image,SaveImagesTag,scene++,
674         GetImageListLength(image));
675       if (status == MagickFalse)
676         break;
677     }while (image_info->adjoin != MagickFalse);
678
679   (void) WriteBlob(image, 4, (const unsigned char *) "fini");
680   (void) WriteBlobLong(image, 0);
681
682 CloseBlob(image);
683 return(MagickTrue);
684 }
685
686