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