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