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